算法 - 二叉树遍历的10种方法,你都会了么?(三)(非递归后序遍历)

书接上回,因为后序遍历是非尾调用的递归,用一个栈实现遍历比较复杂。

下面我们就来讲,使用一个栈的后序遍历怎么实现。

思路一

我们可以采用和先序,中序遍历相同的思路压栈。

但是当我们经过某节点,走向其右子树时,不使用该节点,所以我们应该把该节点再次压栈。

我们访问某节点的条件是:该节点没有孩子,或者右边孩子已经被访问过。

请注意这样一个事实:当我们后序遍历某个结点时,我们上一个访问的元素必然是其右子节点,所以我们可以用pre来记录上一个访问的元素,进行条件判断。

代码如下:

public void traverse(TreeNode root){
    Stack<TreeNode> stack = new Stack<>();
    TreeNode node = root;
    TreeNode pre = null;
    while(node != null){
        stack.push(node);
        node = node.left;
    }
    while(!stack.empty()){
        node = stack.pop();
        if(node.right != null && node.right != pre){
            stack.push(node);
            node = node.right;
                while(node != null){
                    stack.push(node);
                    node = node.left;
                }
        }else{
            //visit 后序             pre = node;
        }
    }
}

这种遍历,有一个好玩的地方,就是:在代码运行到//visit 后序那里时,栈中的元素,刚好是node的所有祖先 。

这是因为:① 当我们遍历某节点时,其“右上方”的任何节点,还没有被经过(pass)。

这其实是‘先序’的入栈方式决定的。

② 当我们遍历右节点时,其左节点必然已经出栈。

这是由后序遍历的出栈方式决定的。

另一种写法:

public void traverse(TreeNode root){
    Stack<TreeNode> stack = new Stack<>();
    TreeNode node = root;
    TreeNode pre = null;
    while(node != null || !stack.empty()){
        if(node != null){
            stack.push(node);
            node = node.left;
        }else{
            node = stack.pop();
            if(node.right != null && node.right != pre){
                stack.push(node);
                node = node.left;
             }else{
                //visit 后序                 pre = node;
                node = null;
            }
        }
    }
}

思路二:

思路一是采用回溯的思想,利用pre节点,把每个父节点入栈(pass)了两次。

那么如果我们从根节点遍历的时候,把右子节点A在其之前入栈,出栈时,通过判断栈顶元素是否为右节点A,就能根据这个条件进入右子树。

代码如下:

public void traverse(TreeNode root){
    if (root == null) return;
        TreeNode cur, pre = null;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.empty()) {
            cur = stack.peek();
            if (pre == null || pre.left == cur ||
                    pre.right == cur) {
                if (cur.left != null)
                    stack.push(cur.left);
                else if (cur.right != null)
                    stack.push(cur.right);
                else {
                    stack.pop();
                    //visit 后序                 }
            } else if (cur.left == pre) {
                if (cur.right != null)
                    stack.push(cur.right);
                else {
                    stack.pop();
                    //visit 后序                 }
            } else if (cur.right == pre) {
                stack.pop();
                //visit 后序             }
            pre = cur;
        }
    }
}

唉,人这一辈子啊,总要有一些看不懂的代码。55555

下一篇,我们看一下Morris遍历,一种不用栈也不用递归的遍历方式~ 还有BFS(广度优先搜索),也就是层次遍历。

    原文作者:李井瑞
    原文地址: https://zhuanlan.zhihu.com/p/34586271
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞