Explain Morris inorder tree traversal without using stacks or recursion

前端 未结 8 1133
别跟我提以往
别跟我提以往 2020-11-27 09:15

Can someone please help me understand the following Morris inorder tree traversal algorithm without using stacks or recursion ? I was trying to understand how it works, but

8条回答
  •  醉话见心
    2020-11-27 09:16

    The recursive in-order traversal is : (in-order(left)->key->in-order(right)). (this is similar to DFS)

    When we do the DFS, we need to know where to backtrack to (that's why we normally keep a stack).

    As we go through a parent node to which we will need to backtrack to -> we find the node which we will need to backtrack from and update its link to the parent node.

    When we backtrack? When we cannot go further. When we cannot go further? When no left child's present.

    Where we backtrack to? Notice: to SUCCESSOR!

    So, as we follow nodes along left-child path, set the predecessor at each step to point to the current node. This way, the predecessors will have links to successors (a link for backtracking).

    We follow left while we can until we need to backtrack. When we need to backtrack, we print the current node and follow the right link to the successor.

    If we have just backtracked -> we need to follow the right child (we are done with left child).

    How to tell whether we have just backtracked? Get the predecessor of the current node and check if it has a right link (to this node). If it has - than we followed it. remove the link to restore the tree.

    If there was no left link => we did not backtrack and should proceed following left children.

    Here's my Java code (Sorry, it is not C++)

    public static  List traverse(Node bstRoot) {
        Node current = bstRoot;
        List result = new ArrayList<>();
        Node prev = null;
        while (current != null) {
            // 1. we backtracked here. follow the right link as we are done with left sub-tree (we do left, then right)
            if (weBacktrackedTo(current)) {
                assert prev != null;
                // 1.1 clean the backtracking link we created before
                prev.right = null;
                // 1.2 output this node's key (we backtrack from left -> we are finished with left sub-tree. we need to print this node and go to right sub-tree: inOrder(left)->key->inOrder(right)
                result.add(current.key);
                // 1.15 move to the right sub-tree (as we are done with left sub-tree).
                prev = current;
                current = current.right;
            }
            // 2. we are still tracking -> going deep in the left
            else {
                // 15. reached sink (the leftmost element in current subtree) and need to backtrack
                if (needToBacktrack(current)) {
                    // 15.1 return the leftmost element as it's the current min
                    result.add(current.key);
                    // 15.2 backtrack:
                    prev = current;
                    current = current.right;
                }
                // 4. can go deeper -> go as deep as we can (this is like dfs!)
                else {
                    // 4.1 set backtracking link for future use (this is one of parents)
                    setBacktrackLinkTo(current);
                    // 4.2 go deeper
                    prev = current;
                    current = current.left;
                }
            }
        }
        return result;
    }
    
    private static  void setBacktrackLinkTo(Node current) {
        Node predecessor = getPredecessor(current);
        if (predecessor == null) return;
        predecessor.right = current;
    }
    
    private static boolean needToBacktrack(Node current) {
        return current.left == null;
    }
    
    private static  boolean weBacktrackedTo(Node current) {
        Node predecessor = getPredecessor(current);
        if (predecessor == null) return false;
        return predecessor.right == current;
    }
    
    private static  Node getPredecessor(Node current) {
        // predecessor of current is the rightmost element in left sub-tree
        Node result = current.left;
        if (result == null) return null;
        while(result.right != null
                // this check is for the case when we have already found the predecessor and set the successor of it to point to current (through right link)
                && result.right != current) {
            result = result.right;
        }
        return result;
    }
    

提交回复
热议问题