十分钟学会二叉树后序遍历循环实现


前言

开头先分享我一个朋友参加某互联网中厂的一面的算法题经历。面试官先是给了一道链表翻转的题目,我朋友思索了片刻便说不会(他刷题刷得确实不多 ),但面试官耐心引导了近20分钟,可惜最后我朋友还是没写出来。

最后没办法,面试官让我朋友写一下二叉树的后序遍历的循环实现,可以看得出来面试官是真的喜欢我这朋友,但是很可惜,我那朋友也没写出来。

我朋友一直给我说二叉树的前序遍历中序遍历循环实现 他都会,唯独后序遍历不会,他也去网上找了一些后序遍历循环实现的讲解文章,发现确实后序遍历在实现上是要复杂一点,因为大多数文章是这么说的。

后序遍历的根节点会经历两次出栈,第一次出栈是为了遍历右子树,第二次出栈进行根节点打印,所以需要记录根节点的状态。

上面的这种后序遍历的循环实现,是目前网上大多数的实现方式,也就是先遍历左子树,遍历左子树之前,根节点入栈(第一次入栈 ),左子树遍历完后,根节点出栈(第一次出栈 ),通过根节点找到右子树,在遍历右子树之前,根节点入栈(第二次入栈 ),右子树遍历完后,根节点出栈(第二次出栈),打印根节点。

其实后序遍历的循环实现有一种更简单且更易于理解的方式,突出一个优雅 ,本文将占用屏幕前的你十分钟的时间,一起来看一下如何简单的完成二叉树后序遍历的循环实现

本文所使用的二叉树节点对象如下所示。

java 复制代码
public class TreeNode {

    public int val;
    public TreeNode left;
    public TreeNode right;

    public TreeNode() {
        
    }

    public TreeNode(int x) {
        this.val = x;
    }

}

正文

首先回顾一下前序遍历 ,前序遍历的遍历顺序是根节点 -> 左子树 -> 右子树,那么前序遍历的循环实现的步骤就如下所示。

步骤一:根节点入栈;
步骤二:如果栈非空则弹出一个节点,否则结束;
步骤三:打印弹出的节点;
步骤四:如果弹出节点的右节点非空,则右节点入栈;
步骤五:如果弹出节点的左节点非空,则左节点入栈;
步骤六:循环步骤二到步骤五。

上述步骤的代码实现如下。

java 复制代码
public static void doTraversalInLoop(TreeNode treeNode) {
    if (treeNode == null) {
        return;
    }

    Stack<TreeNode> treeNodeStack = new Stack<>();
    treeNodeStack.push(treeNode);

    while (treeNodeStack.size() != 0) {
        TreeNode rootNode = treeNodeStack.pop();
        // 打印根节点
        System.out.print(rootNode.val + " ");
        // 先压右
        if (rootNode.right != null) {
            treeNodeStack.push(rootNode.right);
        }
        // 再压左
        if (rootNode.left != null) {
            treeNodeStack.push(rootNode.left);
        }
    }
}

我们可以发现,每弹出一个节点,就会打印这个节点,所以 节点弹出顺序,就是打印顺序,也就是我们的遍历顺序

在上述前序遍历的循环实现中,每次都是 先弹出根节点 ,然后 先压右子节点再压左子节点后压栈的先出栈 ),所以弹出顺序最终就是 根节点 -> 左子节点 -> 右子节点

如果上面的内容已经理解完毕,那么现在我们这样考虑,还是 先弹出根节点 ,但是 先压左子节点再压右子节点后压栈的先出栈 ),所以弹出顺序最终就成了 根节点 -> 右子节点 -> 左子节点 ,此时我们额外使用一个栈结构记作store ,每次弹出的节点就压到store 中,当所有节点遍历完毕后,我们依次弹出store 中的节点并打印,此时store 的弹出顺序就是 左子节点 -> 右子节点 -> 根节点,这个就是后序遍历了。

可以结合下面代码进行理解。

java 复制代码
public static void doTraversalInLoop(TreeNode treeNode) {
    if (treeNode == null) {
        return;
    }

    Stack<TreeNode> stack = new Stack<>();
    stack.push(treeNode);
    Stack<TreeNode> store = new Stack<>();

    while (!stack.isEmpty()) {
        TreeNode rootNode = stack.pop();
        // 将根节点收集起来
        store.push(rootNode);
        // 先压左
        if (rootNode.left != null) {
            stack.push(rootNode.left);
        }
        // 再压右
        if (rootNode.right != null) {
            stack.push(rootNode.right);
        }
    }

    while (!store.isEmpty()) {
        System.out.print(store.pop().val + " ");
    }
}

上述实现,代码十分的简洁,而且整体思想也很简单,就算是背,都能轻易背下来。

总结

核心思路就是我们通过对前序遍历的循环实现进行改造,将节点弹出顺序,由原本前序遍历的 根节点 -> 左子节点 -> 右子节点 ,变为 根节点 -> 右子节点 -> 左子节点 ,然后每个弹出的节点,我们再压到一个额外的栈结构中,当这个额外栈结构弹出节点时,顺序就和入栈顺序是反的,也就成了 左子节点 -> 右子节点 -> 根节点,而这也正是后序遍历的顺序。

分享不易,如果觉得本文对你有帮助,烦请点赞,收藏加关注,谢谢帅气漂亮的你。


相关推荐
开发者每周简报43 分钟前
求职市场变化
人工智能·面试·职场和发展
小灰灰要减肥1 小时前
装饰者模式
java
张铁铁是个小胖子1 小时前
MyBatis学习
java·学习·mybatis
刚学HTML2 小时前
leetcode 05 回文字符串
算法·leetcode
Yan.love2 小时前
开发场景中Java 集合的最佳选择
java·数据结构·链表
椰椰椰耶2 小时前
【文档搜索引擎】搜索模块的完整实现
java·搜索引擎
大G哥2 小时前
java提高正则处理效率
java·开发语言
AC使者2 小时前
#B1630. 数字走向4
算法
冠位观测者2 小时前
【Leetcode 每日一题】2545. 根据第 K 场考试的分数排序
数据结构·算法·leetcode
智慧老师2 小时前
Spring基础分析13-Spring Security框架
java·后端·spring