【二叉树的遍历-3】后序遍历(递归与非递归)

【二叉树的遍历-1】前序遍历(递归与非递归)
【二叉树的遍历-2】中序遍历(递归与非递归)
【二叉树的遍历-4】层序遍历(递归与非递归)

后序遍历

后序遍历(LRD)是二叉树遍历的一种,也叫做后根遍历、后序周游,可记做左右根。后序遍历有递归算法和非递归算法两种。在二叉树中,先左后右再根,即首先遍历左子树,然后遍历右子树,最后访问根结点。

所以后序遍历也只要记住一点::左子树–> 右子树 --> 根结点

递归实现:
和之前的中序遍历和先序遍历差不多,跟套模板一样,改变一下打印的位置就好。

具体代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class  BinaryTree {
    public void postorderTraversal(TreeNode root) {
        if(root != null) {
            postorderTraversal(root.left);
            postorderTraversal(root.right);
            System.out.println(root.val);
        }
    }
}

非递归实现:
跟中序非递归遍历超级像,但是后序遍历比它会多一个步骤,因为当遍历完某个根节点的左子树,回到根节点的时候,对于中序遍历可以把当前根节点直接从栈里弹出,然后转到右子树。而对于后序遍历左右子树遍历完都会回到根节点,所以就需要判断是从左子树到的根节点,还是右子树到的根节点。如果像中序遍历那样不加判断有些情况最后会造成死循环。

  • 所以我们这一次在中序遍历的基础上加一个步骤,增加一个标志位,用它来记录上一次遍历的节点。
  • 如果当前节点的右节点和上一次遍历的节点相同,那就表明当前是从右节点过来的了。此时就可以直接打印当前节点了。
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class  BinaryTree{
    public void postorderTraversal(TreeNode root) {
        Stack<TreeNode> s = new Stack<>();
        TreeNode cur = root;
        TreeNode lastTime = null;  //用它来记录上一次遍历的节点,后面用来和当前节点比较,看看它是不是当前节点的右子树
        while(cur != null || !s.empty()) {  //cur不为空和栈不为空都说明节点没有遍历完,需要继续循环
            while(cur != null) {
                s.push(cur);      //尽可能将节点左子树依次压入栈中
                cur = cur.left;
            }
             //此时栈顶的元素是最左侧的元素,它的左子树为空,相当于左子树已经遍历了,但是不能直接打印。
             //第一次当然很清楚是从左子树到达的根,因为它的左子树刚刚遍历完。
             //但是后面有些有些情况无法判断它是从哪到的根,所以需要判断当前节点的上一个遍历节点是不是其右子树。
            TreeNode temp = s.peek();
            //右子树为空不用遍历可以直接打印,或者刚刚从右子树回来也可以直接打印当前节点
            if(temp.right == null || temp.right == lastTime)  {
                System.out.println(temp.val);
                lastTime = temp;   //每次遍历都用lastTime保存,以便下次遍历时与节点右子树比较。
                s.pop();
            }else {
                cur = temp.right;   //右子树不为空且每遍历,就需要将右子树压栈遍历
            }
        }
    }
}

附leetcode后序非递归遍历代码

题目链接点击直达

代码如下:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        Stack<TreeNode> s = new Stack<>();
        TreeNode cur = root;
        TreeNode lastTime = null;
        while(cur != null || !s.empty()) {
            while(cur != null) {
                s.push(cur);
                cur = cur.left;
            }
            TreeNode temp = s.peek();
            if(temp.right == null || temp.right == lastTime)  {
                ret.add(temp.val);
                lastTime = temp;
                s.pop();
            }else {
                cur = temp.right;
            }
        }
        return ret;
    }
}

对于非递归遍历其实还有一种取巧的做法:

后序遍历的顺序是 :左子树–> 右子树 --> 根结点。
前序遍历的顺序是 根 节点–> 左子树–> 右子树,有这个,我们只需要稍微改变一下前序压栈的顺序就可以轻松的写出:根节点- -> 右子树 --> 左子树 的代码。

然后把:根节点- -> 右子树 --> 左子树逆序,就是左子树–> 右子树 --> 根结点了,也就是后序遍历了。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
      List<Integer> ret = new ArrayList<>();
        if(root == null) {
            return ret;
        }
        Stack<TreeNode> s = new Stack<>();
        s.push(root);
        while(!s.empty()) {
            TreeNode cur = s.pop();
            ret.add(cur.val);
            if(cur.left != null) {
                s.push(cur.left);
            } 
            if(cur.right != null) {
                s.push(cur.right);
            }
        }
        //前面代码都是前序非递归遍历的代码,只是稍微改变了一下左右子树压栈顺序。
        //得到根右左之后,直接反转顺序表,就是后序遍历了。
         Collections.reverse(ret);
         return ret;
    }
}

但是不建议这样写,这个题解只是能返回遍历的结果,并不是严格意义上树拓扑结构的遍历。虽然结果是正确,但是如果真的严格需要按照后续遍历的顺序对树节点进行访问(或操作)时,此解法就无法满足。

发布了27 篇原创文章 · 获赞 57 · 访问量 926

猜你喜欢

转载自blog.csdn.net/A_K_L/article/details/104853772