Non-recursive first-order traversal of binary tree summary (3 methods)

Algorithm non-recursive first-order traversal of binary tree summary (3 methods)

@author:Jingdai
@date:2020.12.03

Portal

Recursive first-order traversal of the binary tree is very simple, but during the interview, the interviewer often asks us to write a non-recursive method, here is a summary.

method 1

Write the simplest method first. First recall the code for traversing the binary tree hierarchically, using a queue to complete the tree traversal. The following code.

public static void levelOrderTraverse(TreeNode root) {
    
    
    if (root == null)
        return;

    LinkedList<TreeNode> queue = new LinkedList<>();
    queue.offer(root);
    while (queue.size() != 0) {
    
    
        TreeNode cur = queue.poll();
        System.out.println(cur.val);
        if (cur.left != null)
            queue.offer(cur.left);
        if (cur.right != null)
            queue.offer(cur.right);
    }
}

What if the queue in the above code is replaced with a stack? The traversal of the tree will still be completed, but the order of traversal is a bit strange. For a node, it will first traverse itself, then traverse its right subtree, and then traverse its left subtree. What is the order of pre-order traversal? First traverse itself, then traverse the left subtree, and then traverse the right subtree, so we can change the stacking order of the left and right subtrees, and the result becomes the following code. Essentially turning the queue into a stack is changing from bfs to dfs.

public static void preOrderTraverse(TreeNode root) {
    
    
    if (root == null)
        return;

    LinkedList<TreeNode> stack = new LinkedList<>();
    stack.push(root);
    while (stack.size() != 0) {
    
    
        TreeNode cur = stack.pop();
        System.out.println(cur.val);
        if (cur.right != null)
            stack.push(cur.right);
        if (cur.left != null)
            stack.push(cur.left);
    }
}

It can also be understood that for the left and right subtrees, the left subtree must be traversed first, and then the right subtree must be traversed. The stack is last-in, first-out. We traverse when we are out of the stack, so we traverse first and then enter the stack, so we enter the stack later. Left subtree.

Method 2

The following introduces a slightly more complicated method, which is the method introduced in the general data structure book.

The first order is the traversal at the first encounter; the middle order is the traversal with the second encounter; the post-sequence is the traversal with the third encounter. Take a look at the code.

public static void preOrderTraverse(TreeNode root) {
    
    

    TreeNode p = root;
    LinkedList<TreeNode> stack = new LinkedList<>();
    while (p != null || stack.size() != 0) {
    
    
        while (p != null) {
    
    
            System.out.println(p.val);
            stack.push(p);
            p = p.left;
        }
        p = stack.pop();
        p = p.right;
    }
}

For each node, it is traversed when it meets, and then its left subtree is traversed. When the node has no left subtree, pop an element so that p points to its right subtree, and then traverse its right subtree in the same way.

Method 3 Morris method

The time complexity of the above methods is all O(n), and the space complexity is O(h)that the recursive method is the same, but the stack space of the system is used. Morris response to this problem, a space for the complex O(1)approach.

First, let's look at the process of Morris traversal. (Refer to Zuo Shen's thinking) This is not for the pre-order, the pre-order is changed a bit on the basis of this traversal, and then it becomes the pre-order.

For the node currently being traversed cur, there are the following two situations.

  1. When curthere is no left subtree, directly curmove to its right subtree: cur = cur.right.

  2. When curwhen the left sub-tree, to find the rightmost node in the left subtree rightmost:

    • If rightmost.right == null, here is a description that rightmost.rightpoint cur, and then curmoved to its left subtree:

      rightmost.right = cur; cur = cur.left;

    • If rightmost.right == cur, herein described is the second to the rightmost.rightpoint recovery is null, then curmoved to its right subtree:

      rightmost.right = null; cur = cur.right;

When curpointing null, the loop exits.

The final code is as follows:

public static void morrisOrderTraverse(TreeNode root) {
    
    

    TreeNode cur = root;
    TreeNode rightmost = null;
    while (cur != null) {
    
    
        if (cur.left != null) {
    
    
            rightmost = cur.left;
            while (rightmost.right != null && rightmost.right != cur) {
    
    
                rightmost = rightmost.right;
            }
            // the first time to come here
            if (rightmost.right == null) {
    
    
                rightmost.right = cur;
                System.out.println(cur.val);
                cur = cur.left;
            } else {
    
     // seconde time to come here
                rightmost.right = null;
                System.out.println(cur.val);
                cur = cur.right;
            }
        } else {
    
    
            System.out.println(cur.val);
            cur = cur.right;
        }
    }
}

This is the sequence of Morris order, so how to change it to preorder? Pre-order is to traverse the first time it meets. In Morris traversal, if a node has a left child, it will traverse twice (the first time the rightmost node of the left child points to null, and the second time the rightmost node of the left child points to This node); if a child does not have a left child, it will only be traversed once (because null does not traverse, counting null back and forth is also twice). So we can traverse as long as we encounter a node for the first time, that is, if a node has a left child, it is traversed at the first encounter; if a node does not have a left child, it is traversed directly. Finally, Morris's pre-order traversal code is as follows.

public static void preOrderTraverse(TreeNode root) {
    
    

    TreeNode cur = root;
    TreeNode rightmost = null;
    while (cur != null) {
    
    
        if (cur.left != null) {
    
    
            rightmost = cur.left;
            while (rightmost.right != null && rightmost.right != cur) {
    
    
                rightmost = rightmost.right;
            }
            // the first time to come here
            if (rightmost.right == null) {
    
    
                rightmost.right = cur;
                System.out.println(cur.val);
                cur = cur.left;
            } else {
    
     // the second time
                rightmost.right = null;
                cur = cur.right;
            }
        } else {
    
    
            System.out.println(cur.val);
            cur = cur.right;
        }
    }
}

In the same way, the in-order traversal based on the Morris method is also easy to write, that is, the node is traversed the second time it meets. If a node does not have a left subtree, it is traversed directly; if a node has a left subtree, it is traversed again when it is encountered for the second time.

Test code

To facilitate testing, you can use the code here to create a tree and test.

public static void main(String[] args) {
    
    

    // create tree
    TreeNode root = new TreeNode(1);
    TreeNode node2 = new TreeNode(2);
    TreeNode node3 = new TreeNode(3);
    TreeNode node4 = new TreeNode(4);
    TreeNode node5 = new TreeNode(5);
    TreeNode node6 = new TreeNode(6);
    TreeNode node7 = new TreeNode(7);
    root.left = node2;
    root.right = node3;
    node2.left = node4;
    node2.right = node5;
    node3.left = node6;
    node3.right = node7;

    preOrderTraverse(root);
}
class TreeNode {
    
    
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) {
    
     val = x; }
}

Guess you like

Origin blog.csdn.net/qq_41512783/article/details/110524693