非递归实现二叉树的后序遍历

力扣链接

递归本质其实就是运用了栈这个数据结构,虽然我们不使用递归,但是任然需要用到栈这个数据结构,只是这个数据结构需要我们自己维护,相比于递归,他的时间空间开销肯定会更小。

思路:

事前,先把变量定义好

具体用法,下面来慢慢讲。

因为是后序遍历,我们先让cur一直朝着左边走,走到null,同时顺带把当前的节点压入栈中

以当前树为例

执行完上述所有代码后:


因为是后序遍历,我们要想办法,访问到E结点。

这是的栈顶元素就派上了用场:

top.right就是节点E。

当cur结点一直left,到达空后,还有另一种情况,就是D的右节点是空的。

既然是后序遍历,D的两个结点都是空,那就把D结点记录到list中呗,然后从栈中pop掉D结点(因为D结点以及其子树已经全部访问完)。

整体代码如下:

 //先一直遍历左子树,直到cur是空的
            while (cur != null) {
                stack.push(cur);//把遍历的节点,都压入栈中
                cur = cur.left;
            }
            //到了这里,cur已经是左边的空了,不要pop栈,因为要先访问右子树,然后再访问根节点
            top = stack.peek();//先得到上一个节点,进而访问右节点
            if (top.right == null) {//右节点也是空的,那么就记录当前根节点,然后弹出此节点
                list.add(top.val);
                stack.pop();
            }else{//右节点不为空
                cur=top.right;
            }

用下面的树来演示一遍:

top.right不是空,是E

走完代码之后,我们发现了一个尴尬的窘境,走到E,程序就结束了,list什么都没有存。

为了一直循环操作下去,还需要再整体代码最外层加上一个循环

  while (cur != null || !stack.isEmpty()) {
//cur只要不为空就一直遍历
//栈不为空,需要把栈中所有的节点都访问完才行

        }

在按照下面代码演示一遍(外层循环执行一次,变换一个图片):

       while (cur != null || !stack.isEmpty()) {

            //先一直遍历左子树,直到cur是空的
            while (cur != null) {
                stack.push(cur);//把遍历的节点,都压入栈中
                cur = cur.left;
            }
            //到了这里,cur已经是左边的空了,不要pop栈,因为要先访问右子树,然后再访问根节点
            top = stack.peek();//先得到上一个节点,进而访问右节点
            if (top.right == null) {//右节点也是空的,那么就记录当前根节点,然后弹出此节点
                list.add(top.val);
                stack.pop();
            }else{//右节点不为空
                cur=top.right;
            }

        }

第一次循环:

第二次循环:

第三次循环:


唉,我在这里就没有画了。

细心的大伙儿,不知道有没有发现,第三个循环的图和上面的某一个循环的图长的很像。

没错,就是第一个循环!

除了List,存的元素不一样,其他变量都一样,如果我在画下去,其实就没有意义了,就是在这三张图里一直兜圈子,而list只记录了E,最终的结果是,list一E到底。——没错死循环了!

不要怕,有时,在最困难的时刻,在坚持一小会儿,就会看到胜利的╰(*^▽^*)╯

想要修改其实也很简单。

循环的原因很简单,就是E明明被记录了,但是程序还是会通过top.right去访问E,形成死循环。

我们只需要对记录过的节点进行标记就可以了。

方法:

定义这样一个变量:

一旦list要记录一个节点,就把这个节点赋值给pre。

然后再把上面代码的if条件多加一条

如果当前节点的左节点已经记录了,那么记录当前节点,然后pop当前节点(右遍历完,然后根节点遍历)

这样就大功告成啦

整体代码:

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {


        Stack<TreeNode> stack = new Stack<>();//借助栈来实现后序遍历
        List<Integer> list = new ArrayList<>();//用来记录节点的顺序
        TreeNode top = null;//接收peek的元素
        TreeNode cur = root;//遍历整棵树
        TreeNode pre = null;
        while (cur != null || !stack.isEmpty()) {

            //先一直遍历左子树,直到cur是空的
            while (cur != null) {
                stack.push(cur);//把遍历的节点,都压入栈中
                cur = cur.left;
            }
            //到了这里,cur已经是左边的空了,不要pop栈,因为要先访问右子树,然后再访问根节点
            top = stack.peek();//先得到上一个节点,进而访问右节点
            if (top.right == null||pre==top.right) {//右节点也是空的,那么就记录当前根节点,然后弹出此节点
                list.add(top.val);
                pre=top;
                stack.pop();
            }else{//右节点不为空
                cur=top.right;
            }

        }
        return list;
    }
}

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值