简体   繁体   中英

How to get all leaf nodes from any parent node in a tree

I am developing an app in android using Sqlite, I have a tree structure which I represent in the db like so :

        +------+------+-------+------+
        |comp_id nodeId parent| text |
        |------|------|-------|------|
        |  146 |  1   |  -1   | Top  |
        |      |      |       |      |
        | 146  |  2   |  1    | Ch1  |
        |      |      |       |      |
        | 146  |  3   |  2    | Leaf |
        |      |      |       |      |
        |  ... |      |       |      |
        | 152  |  1   |  -1   | Top  |
        +------+------+-------+------+

I am having difficulty coding an algorithm in a self contained method like below to return me all the leafs under any node.

Node
{
   public Node[] getAllLeafs()
   {
      // traverse all the way down the tree
      // and get only leafs
   }
}

If there is a way to accomplish this more easily by modifying my table structure and/or using SQL please mention that as I am able to do so.

Select all rows whose node_id s are not parents of some other rows, ie they are leaves.

select *
from   tree_table
where  node_id not in (
   select distinct parent from tree_table
);

You can make a recursive method, that would return an array of all the leafs from its children, if a node has children, or the Node itself, if it doesn't have children, therefore is a leaf itself.

public Node[] getAllLeafs() {

    ArrayList<Node> allLeafs = new ArrayList<Node>();

    if (getAllChildren().size() == 0) {
        allLeafs.add(this);
        return (Node[]) allLeafs.toArray();
    } else {
        for (Node child : this.getAllChildren()) {
            allLeafs.addAll(child.getAllLeafs());
        }
    }
}

This way you can keep the logic within one method and don't have to redundantly traverse unimportant nodes. The implementation of the structural methods is up to you.

I think this logic would work:

select node_id
from tree_table where node_id not in 
(select a.node_id 
from tree_table as a, tree_table as b
where a.node_id = b.parent); 

The inner query finds out those nodes which are also parents in the same table. The outer query finds out the nodes which are not parents of any node and hence must be leaf nodes. Hope this helps!

Well, what is the reason to fetch all nodes under instead of all direct nodes? Second option is obviously simple.

I don't think that there is a reasonable solution, maybe graph databases. The point is that you can either:

  1. To have all parent ids (direct and non-direct) persisted along with every node. You could design new table having foreign keys for that. But this solution seems like heavyweight. Speaking of which such table should grow exponentially.

  2. To fetch the whole tree together . This is the approach we are using within our app after four years of database research. DB is designed to fetch data lying next to each other because of buffering and reading of hard-drive blogs ahead. You can improve this solution using correct indexes and so on. What I'm saying is that sometimes it's better approach to fetch continuous drive segment from DB than making complex queries and than make searching for all nodes in java code.

    Note that one fetch of the whole tree (by root index id) can do DB within one DB read. On the other hand, usually a select using nested query or using joins uses more reads, requires ids matching, maybe temporary table and so on. (index handling, locking, etc.)

    You should definitely use various NoSQL DBs but also RDBMS.

With SQLite 3.8.3 or later, you can use a query like the following to compute the subtree beginning at a specific node, and then get the leaves in that subtree:

WITH RECURSIVE subtree(comp_id, nodeId, parent, text)
AS (SELECT *
    FROM MyTable
    WHERE comp_id = 146 AND nodeId = 1  -- start node
    UNION ALL
    SELECT MyTable.*
    FROM MyTable
    JOIN subtree ON MyTable.comp_id = subtree.comp_id
                AND MyTable.parent  = subtree.nodeid)
SELECT *
FROM subtree
WHERE nodeid NOT IN (SELECT parent
                     FROM subtree)

With earlier SQLite versions, you would have to retrieve the nodes at each level by hand.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM