LeetCode 145. Binary Tree Postorder Traversal

问题描述

  • Given a binary tree, return the postorder traversal of its nodes’ values.
  • Note: Recursive solution is trivial, could you do it iteratively?
  • 地址

问题分析

  • 实现二叉树的后序遍历

    • 递归
    • 非递归

      • 教科书法(最规范,最符合后序遍历的过程)

      • 法2

      • 类似于先序遍历得到根左右的情况,现在先得到根右左的结果(先压左,后压右),然后逆序左右根
      • 金手指法

代码实现

  • 递归
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        postorderTraversal(root, res);
        return res;
    }

    public void postorderTraversal(TreeNode root, List<Integer> res) {
        if (root == null) {
            return;
        }
        postorderTraversal(root.left, res);
        postorderTraversal(root.right, res);
        res.add(root.val);
    }
  • 非递归教科书法(最规范,最符合后序遍历的过程)
    public List<Integer> postorderTraversal(TreeNode root) {
        if (root == null) {
            return new ArrayList<Integer>();
        }
        Stack<TreeNode> stack = new Stack<>();
        List<Integer> res = new ArrayList<>();
        stack.push(root);
        //注意!!!这个初始值不具有lastPop的意思,只是为了正确开启循环罢了
        TreeNode lastPrinted = root;
        TreeNode peek = null;
        while (! stack.isEmpty()) {
            peek = stack.peek();
            if (peek.left != null && lastPrinted != peek.left && lastPrinted != peek.right) {
                //若上一个打印的节点既不是左孩子,也不是右孩子,那么压左孩子
                stack.push(peek.left);
            }else if (peek.right != null && lastPrinted != peek.right) {
                //经过上一个if的过滤,若上一个打印的节点不是栈顶的右孩子,压右孩子
                stack.push(peek.right);
            }else {
                //打印栈顶元素,出栈,并更新 lastPrinted
                res.add(peek.val);
                lastPrinted = stack.pop();
            }
        }
        return res;
    }
  • 非递归法2
     public List<Integer> postorderTraversal(TreeNode root) {
        if (root == null) {
            return new ArrayList<Integer>();
        }
        Stack<TreeNode> stack = new Stack<>();
        List<Integer> res = new ArrayList<>();
        stack.push(root);
        //初始化
        TreeNode lastPrinted = null;
        while (! stack.isEmpty()) {
            //当前栈顶元素
            TreeNode peek = stack.peek();
            if (peek.left == null && peek.right == null) { 
                //当前元素无左右孩子,打印出栈,并更新lastPrinted
                res.add(stack.pop().val);
                lastPrinted = peek;
            }else if (lastPrinted != null && (lastPrinted == peek.left || lastPrinted == peek.right)) {
                /*
                如果上一个打印的节点是当前栈顶元素的左孩子或者右孩子,说明该节点的左孩子和右孩子都已被访问过了,(因为压栈时保证了左右根的顺序)
                则同样可以直接访问该结点,lastPrinted
                加lastPrinted == peek.left 的原因可能是当前节点无右孩子,同理加preNode == curNode.right
                lastPrinted != null的原因是lastPrinted初始化是null,若不加,则当满足后面两个条件之一时,会打印节点,但这样是不对的
                */
                res.add(stack.pop().val);
                lastPrinted = peek;
            }else {
                /*
                若存在左孩子和右孩子,先将右孩子入栈,再将左孩子入栈,保证了 每次取栈顶元素的时候,
                左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。
                */
                if (peek.right != null) {
                    stack.push(peek.right);
                }
                if (peek.left != null) {
                    stack.push(peek.left);
                }
            }
        }
        return res; 
     }
  • 先根右左,然后逆序成左右根,注意逆序也可以不用栈
    public List<Integer> postorderTraversal(TreeNode root) {
         if (root == null) {
            return new ArrayList<Integer>();
        }
        Stack<TreeNode> s1 = new Stack<>();
        //s2存的是左右根的结果,用来逆序
        Stack<TreeNode> s2 = new Stack<>();
        s1.push(root);
        while (!s1.isEmpty()) {
            //出栈便“打印”,此刻打印是装入s2
            TreeNode popNode = s1.pop();
            s2.push(popNode);
            //将出栈节点先左后右(如果非空)入栈
            if (popNode.left != null) {
                s1.push(popNode.left);
            }
            if (popNode.right != null) {
                s1.push(popNode.right);
            }
        }
        List<Integer> res = new ArrayList<>();
        //装入res
        while (!s2.isEmpty()) {
            res.add(s2.pop().val);
        }
        return res;
    }
  • 金手指法
    public List<Integer> postorderTraversal(TreeNode root) {
        if (root == null) {
            return new ArrayList<Integer>();
        }
        List<Integer> res = new ArrayList<>();
        Stack<Command> stack = new Stack<>();
        //initial
        stack.push(new Command(false, root));
        while(! stack.isEmpty()) {
            Command curCommand = stack.pop();
            TreeNode curRoot = curCommand.root;
            if (curCommand.isPrint) {
                res.add(curRoot.val);
            }else {
                //命令入栈顺序是根(打印)右(遍历)左(遍历)
                //所以出栈顺序是左(遍历)右(遍历)根(打印),符合后序遍历的顺序
                stack.push(new Command(true, curRoot));
                if (curRoot.right != null) {
                    stack.push(new Command(false, curRoot.right));
                }
                if (curRoot.left != null) {
                    stack.push(new Command(false, curRoot.left));
                }
            }
        }
        return res;
    }

}

//用于指示对root执行什么操作
class Command{
    boolean isPrint;
    TreeNode root;
    public Command(boolean isPrint, TreeNode root) {
        this.isPrint = isPrint;
        this.root = root;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值