算法-优雅的进行二叉树的后序遍历

优雅的进行二叉树的后序遍历

树的遍历是一个常用的操作,分为递归和非递归遍历,经典的递归遍历如下

	List<Integer> lists=new ArrayList<>();
    /**
     * 后序递归
     * @param node
     * @return
     */
    public List<Integer> backRecuErgodic(TreeNode node){
        if(node!=null){
            backRecuErgodic(node.left);
            backRecuErgodic(node.right);
            lists.add(node.val);
        }
        return lists;
    }

这种方式非常简洁,而且前序,中序,后序遍历的格式几乎完全统一,很好记忆

不过在非递归的情况下,大家的解法就复杂了起来,对于前序和中序遍历的非递归实现,代码风格是一致的,而对于后序遍历,大家的解法就复杂多样了

    /**
     * 先序非递归
     * @param node
     * @return
     */
    public List<Integer> preNoRecuErgodic(TreeNode node){
        Stack<TreeNode> stack=new Stack<>();
        while (node!=null||stack.isEmpty()==false){
            if(node!=null){
                lists.add(node.val);
                stack.push(node);
                node=node.left;
            }else {
                node=stack.pop();
                node=node.right;
            }
        }
        return lists;
    }
    
    /**
     * 中序非递归
     * @param node
     * @return
     */
    public List<Integer> inNoRecuErgodic(TreeNode node){
        Stack<TreeNode> stack=new Stack<>();
        while (stack.isEmpty()==false||node!=null){
            if(node!=null){
                stack.push(node);
                node=node.left;
            }else {
                node=stack.pop();
                lists.add(node.val);
                node=node.right;
            }
        }
        return lists;
    }

其实后序遍历也可以有这样统一的格式,试一下

			1
	 2			   3
4		5		6		7

仔细观察一下上面的树,其后序遍历的结果为

4 5 2 6 7 3 1

前序遍历的结果为

1 2 4 5 3 6 7

若我们换个思路,按照前序遍历的思想,只不过我们先遍历右儿子,那遍历结果为

1 3 7 6 2 5 4

怎么样,是不是和后序遍历结果很像?按照先序遍历的规则,把先遍历左儿子转为先遍历右儿子,我们就可以得到一个神奇的结论,这种规则情况下的遍历的操作,得到的结果是后序遍历的相反的顺序。

其实后序遍历和前序遍历有着一种镜像关系,后序遍历可以有原来树的镜像树(镜像树可以看leetcode上的一道题)的前序遍历的逆序得到

于是,我们可以得到一个与前序和后序遍历同样形式的代码

	List<Integer> lists=new ArrayList<>();
    public List<Integer> backNoRecuErgodic(TreeNode node){
        Stack<TreeNode> stack=new Stack<>();
        while (stack.isEmpty()==false||node!=null){
            if(node!=null){
                stack.push(node);
                lists.add(0,node.val);//头插正好逆序。后序遍历和前序遍历是镜像问题
                node=node.right;
            }else {
                node=stack.pop();
                node=node.left;
            }
        }
        return lists;
    }

不过需要注意的是,ArrayList底层是使用数组实现的,头插的效率较低,需要经过数组拷贝,因此插入头部的复杂度为O(N)

LinkedList是双向链表,插入头部的时间复杂度是O(1)于是我们可以用LinkedList来代替ArrayList,这样效率更高

	List<Integer> lists=new LinkedList<>();
    public List<Integer> backNoRecuErgodic(TreeNode node){
        Stack<TreeNode> stack=new Stack<>();
        while (stack.isEmpty()==false||node!=null){
            if(node!=null){
                stack.push(node);
                lists.offerFirst(node.val);//头插正好得到逆序。
                node=node.right;//这里先去遍历的右儿子
            }else {
                node=stack.pop();
                node=node.left;
            }
        }
        return lists;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值