Three ways of in-order traversal (recursive, non-recursive, non-recursive constant space)

table of Contents

1. Recursion: Ideas and Algorithms

2.Non-recursive (using the stack to simulate recursion): ideas and algorithms

3. Non-recursive constant space: ideas and algorithms


1. Recursion: Ideas and Algorithms

First of all, we need to understand what is the in-order traversal of a binary tree: traverse the tree in the way of visiting the left subtree-the root node-the right subtree, and we follow the same way when visiting the left subtree or the right subtree Traverse until the complete tree is traversed. Therefore, the entire traversal process is naturally recursive, and we can directly simulate this process with a recursive function.

class Solution {
    public List<Integer> middleTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        minorder(root, res);
        return res;
    }
//如果节点不为空,遍历左子树,直到叶子结点,加入结果集,再遍历该节点的右子树
    public void minorder(TreeNode root, List<Integer> res) {
        if (root == null) {
            return;
        }
        minorder(root.left, res);
        res.add(root.val);
        minorder(root.right, res);
    }
}

2.Non-recursive (using the stack to simulate recursion): ideas and algorithms

Until we are in the sequence are arranged in order: 左节点,根节点,右节点. Then we cannot release the front node after passing the root node, because it will be used later. So be 用栈先储存.
Its rules are roughly as follows:

  • Stack 依次存入左节点所有点until the leftmost is at the top of the stack.
  • Start 抛出栈顶并访问.

Feasibility analysis: the middle order is 左—中—右the order. After visiting the left. When the current point is thrown, it means that the left side has been visited (or it is the left side), then the right side of the current point needs to be visited first. Then this 右节点把它当成根节点重复相同操作(because the right node must meet the order of left and then right). This actually simulates a recursive process, and you need to think about it yourself.

  //中序遍历,左-根-右
    //利用栈来存储节点,当当前节点不为空的时候,将当前节点压入栈中,然后找到最左边的叶子,否则root=当前节点,寻找右子树的最左节点,循环下去,知道栈为空,root也为空    
public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res=new ArrayList<>();
        Stack<TreeNode> stack=new Stack<>();
        while(!stack.isEmpty()||root!=null){
            if(root!=null){
                stack.push(root);
                root=root.left;
            }else{
                root=stack.pop();
                res.add(root.val);
                root=root.right;
            }       
        }
        return res;
    }

3. Non-recursive constant space (Morris): ideas and algorithms

This is what I encountered when looking at the solution of the problem when I was trying to brush the problem. This algorithm uses the rightmost leaf node of the left subtree of the root node to point to the root node, that is, to find the precursor of the root.

Morris traversal algorithm is another method of traversing binary trees, which can reduce the space complexity of non-recursive in-order traversal to O(1)O(1).

The overall steps of Morris traversal algorithm are as follows (assuming that the current traversed node is xx):

If xx has no left child, first add the value of xx to the answer array, and then visit the right child of xx.

If xx has a left child, find the rightmost node on the left subtree of xx (that is, the last node in the middle order traversal of the left subtree, and the predecessor node of xx in the middle order traversal), and we record it as the predecessor. According to whether the right child of the predecessor is empty, proceed as follows.

If the right child of the predecessor is empty, point its right child to xx, and then visit the left child of xx, that is, x = x.left.

If the right child of the predecessor is not empty, then its right child points to xx, indicating that we have traversed the left subtree of xx, we empty the right child of the predecessor, add the value of xx to the answer array, and then visit the xx The right child, ie x == x.right.

Repeat the above operations until the complete tree is visited.

Source: LeetCode

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        TreeNode predecessor = null;

        while (root != null) {
            if (root.left != null) {
                // predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止
                predecessor = root.left;
                while (predecessor.right != null && predecessor.right != root) {
                    predecessor = predecessor.right;
                }
                
                // 让 predecessor 的右指针指向 root,继续遍历左子树
                if (predecessor.right == null) {
                    predecessor.right = root;
                    root = root.left;
                }
                // 说明左子树已经访问完了,我们需要断开链接
                else {
                    res.add(root.val);
                    predecessor.right = null;
                    root = root.right;
                }
            }
            // 如果没有左孩子,则直接访问右孩子
            else {
                res.add(root.val);
                root = root.right;
            }
        }
        return res;
    }
}

 

Guess you like

Origin blog.csdn.net/lovekjl/article/details/108572374