【算法】二叉树的前中后序遍历 (递归与迭代写法)

一、分清前、中、后序遍历
看根节点遍历的顺序,左子节点一定在右子节点之前遍历 :

  • 前序 : 根左右
  • 中序 : 左根右
  • 后序 : 左右根

二、前序遍历
递归写法 : 直接看代码 :
在这里插入图片描述
迭代算法 :
众所周知,把递归改成迭代就需要手动去维护一个栈,关键就在于理清栈的入栈出栈顺序。首先我们一定要搞清楚一点,栈是先进慢出的,因此,如果我们想要先遍历左子节点,就要先把右子节点入栈。
前序遍历的顺序是:根左右。当我们出栈时,得到的元素就是一个根节点 (记为node)。然后我们先把node的右子节点入栈,再把左子节点入栈,这样等到出栈的时候就是先出左子节点、再出右子节点,符合前序遍历的顺序。
当理不清出入栈顺序时,手动画一个栈模拟一下就能清楚。
在这里插入图片描述
三、中序遍历
递归写法:与前序遍历的写法类似,这里就不写了。
迭代写法:
(1) 前序遍历时,访问的第一个节点就是我们要处理的第一个节点,而中序遍历不一样,我们访问的第一个节点 (根节点)并不是我们要处理的第一个节点 (最左的节点)。事实上,我们访问到的每一个节点都是中间节点。因此,在处理节点之前我们需要先找到这个“最左”的节点,这需要一次循环,不断向左走,边走边把遇到的节点都入栈。
(2)在我们出栈时,也就是执行root = myStack.pollLast();这条代码时候,root.left这棵树已经完全遍历完了,因此我们此刻访问的是中间节点,访问完中间节点之后进行赋值root = root.right,目的是在下次循环的时候完成root.right这棵树的遍历。
(3) 最外层循环的循环条件:①首先,root!=null这点跟递归写法的意义类似。② !myStack.isEmpty(),栈非空代表还有一些节点没被遍历到。
在这里插入图片描述
(4) 与递归写法联系一下:
在这里插入图片描述
参考资料:https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/dai-ma-sui-xiang-lu-che-di-chi-tou-qian-xjof1/

四、后序遍历
递归写法:与前序遍历的写法类似,这里就不写了。
迭代写法:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if(root == null) return res;
        
        Deque<TreeNode> stk = new LinkedList<>();
        TreeNode prev = null;
        //循环的时候牢记:root代表当前访问的节点;栈stk中的是还没有被访问的节点
        //此处的判断条件是:当前访问的不是空节点 && 栈中还有节点没被访问到
        while(root != null || !stk.isEmpty()){
            //这个while循环及出栈操作的目的是:找到最左的节点            
            while(root != null){
                stk.offerLast(root);
                root = root.left;
            }
            root = stk.pollLast();
            //判断当前访问的这个节点(root)是否可以被放进后序序列中
            //因为后序序列是:左右根,所以必须是右节点访问过了才能访问root
            if(root.right == null || root.right == prev){
                res.add(root.val);
                prev = root;
                root = null;
            }else{
                //如果root.right还没被访问过,那么root此时还不能被放入后序序列中,因此把它入栈
                //入栈之后把root赋值为root.right,这是因为下次遍历要从root.right开始
                stk.offerLast(root);
                root = root.right;
            }

        }
        return res;
    }
}

这里注意一下root = null这句代码,因为后序遍历的顺序是:左右根。因此当处理完根节点之后,需要把root置null,这样下次进来循环的时候才会从栈中弹出节点;否则就会继续向左搜索,陷入死循环。
也可以这样理解:当我们访问完根节点root的时候,说明root.left已经全部访问完了(根据后序遍历的定义),因此我们不希望下次遍历会继续往root的左边去了,所以内循环的这个while循环是没有意义的,因此我们把root置为null,避免再次进入内循环。
参考资料:https://leetcode-cn.com/problems/binary-tree-postorder-traversal/solution/er-cha-shu-de-hou-xu-bian-li-by-leetcode-solution/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值