简体   繁体   中英

java alogrithm: find pre/next leaf node that meet special condition from any node in a tree

I need to write a function to find previous/next leaf node that meet special condition from any node in a singly rooted tree. (in the parent first order)

The API would be something like this:

Node findNextLeafNode(Node currentNode, Condition condition);
Node findPretLeafNode(Node currentNode, Condition condition);

where currentNode is any node in a tree, and Node is defined as:

interface Node{
    /** @return the Node's parent or null if Node is root */ 
    Node getParent();

    /** @return true if node is root */
    boolean isRoot();

    /** @return non-null array of child nodes (zero length for leaf nodes) */
    Node[] getChildren();

    /** @return the number of child nodes. If node is leaf, value is 0 */
    int getChildCount();
}

And the Condition interface defines the semantics of checking a constraint against a given Node .

interface Condition{
  /** @return true if provided node meets the condition */
  boolean check(Node node);
}

My question:

Is there an existing library or algorithm for such a common scenario? I am open to either stack based or recursive algorithms. Pseudocode, links to open source libraries, or if you care to share you own code, would be appreciated.

(If not, I need to spend time to invent the same wheel again and paste it here later for sharing.)

Thanks.

-----------------------------write a method to getNext()........

// currentNode must be descendant of root
public static Node getNextNode(Node currentNode, Node root)
{
    // 1. if is has child, next is its first child
    if (currentNode.getChildSize() > 0) {
        return currentNode.getChildren()[0];
    }
    // 2. if it has no child, check if its is the last child of his parent
    else {
        // if it is root and has no children, return null
        if (currentNode == root) {
            return null;
        }

        // else should have parent which is or under root;
        Node parent = currentNode.getParent();
        int index = getIndex(currentNode);

        if (!isLastofParent(currentNode)) {
            // ----a. if not last, next is his parent's next
            return currentNode.getParent().getChildren()[index + 1];
        }
        else {
            // ----b. if it is the last one, return its parent's next right if there is. while until root
            Node tmp = parent;
            while (tmp != root) {
                int parentIndex = getIndex(tmp);
                if (!isLastofParent(tmp)) {
                    return tmp.getParent().getChildren()[parentIndex + 1];
                }
                tmp = tmp.getParent();
            }
        }
    }
    return null;

}

private static boolean isLastofParent(Node node)
{
    if (getIndex(node) == node.getParent().getChildSize() - 1) {
        return true;
    }
    return false;
}

private static int getIndex(Node currentNode)
{
    Node parent = currentNode.getParent();
    for (int i = 0; i < parent.getChildSize(); i++) {
        if (parent.getChildren()[i] == currentNode) {
            return i;
        }
    }
    //TODO: error condition handling, will not happen if tree not change
    return -1;
}

------------------------a full search is much easier............

public static Node getNextFailNode(Node currentNode, Node root, Condition condition)
{
    boolean foundCurrentNode = false;
    Stack<Node> stack = new Stack<Node>();
    stack.push(root);
    while (!stack.isEmpty()) {
        Node tmp = stack.pop();
        System.out.println("-popup---------" +tmp+ " ");
        if (foundCurrentNode && checkCondition(tmp, condition)) {
            return tmp;
        }
        if (tmp == currentNode) {
            foundCurrentNode = true;
        }
        if (tmp.getChildSize() > 0) {

            for (int i = tmp.getChildSize() - 1; i >= 0; i--) {
                stack.push(tmp.getChildren()[i]);
            }
        }

    }
    return null;
}

This maybe way overblown for what you need, but it can support what you want:

There is a graph traversal language: Gremlin . Typically bolted on top of something like Neo4j, but any graph data structure (eg a singly rooted directed tree) can be wrapped to support the API. Take a look at Blueprints projects to find out how it is done.

[edit: for something less heavy]

Perhaps JGraphT is what you want. Also take a look at this question on SO . It is not an exact duplicate, but you should find it helpful.

Write an iterator for your tree that can be initialized from any node and uses pre/in/post-order traversal (Of course it should be bi-directional). This is basically writing one simple algorithm that at least to me seem basic. Once you have an iterator all you need is to iterate your way to the next node which is a leaf and the condition holds for it. If you have trouble with any specific part just ask and I'll improve my answer.

Based on the fact that you already have defined your interfaces, and you say the graph-traversal libraries are too heavyweight, you probably should just write it yourself. It would be an absolutely trivial amount of code. ( This page contains some code if you need help.)

(One suggestion for your API: don't put a boolean isRoot(); method on Node , that's a waste of bits unless you have a very good reason to do so. The code that builds the tree should just refer to the root node.)

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