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


前言

开头先分享我一个朋友参加某互联网中厂的一面的算法题经历。面试官先是给了一道链表翻转的题目,我朋友思索了片刻便说不会(他刷题刷得确实不多 ),但面试官耐心引导了近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 + " ");
    }
}

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

总结

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

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


相关推荐
暴力求解4 分钟前
C++ ---继承(二)
开发语言·c++·算法
猫猫.1539 分钟前
IO、存储、硬盘、文件系统详细总结
java·java-ee
SL_staff28 分钟前
设备离线时业务不中断:JVS-IOT高可用架构解析
java·物联网·产品
码林拾遗WJ28 分钟前
告警分级的自动化实测:模型判断 vs 人工拍板
人工智能·算法·自动化
郭小李42 分钟前
mybatis 动态 sql
java·sql·mybatis
Tisfy42 分钟前
LeetCode 0453.最小操作次数使数组元素相等:两次遍历(反向思维)
算法·leetcode·题解·遍历·反向思维
瑞码空间1 小时前
JSP与Servlet核心开发与CRUD实战笔记
java·笔记·servlet·jsp
知守观1 小时前
技术考古:iBATIS 2.3.4 时代的持久层,是如何用 Map 玩转 307 个存储过程的?
java·后端·mybatis
君万1 小时前
【LeetCode每日一题】回溯:46. 全排列 78. 子集 39. 组合总和
算法·leetcode·go
Keep_Trying_Go2 小时前
Consistent-Point半监督人群计数算法详解
人工智能·深度学习·算法·计算机视觉·人群计数