二叉树的统一迭代遍历

核心思想:给需要"收割"的节点贴上标签

统一迭代法的精髓在于引入了一个特殊的标记(null 指针)

在遍历过程中,我们会遇到两种状态的节点:

  1. 第一次路过(要排队):只看到了这个节点,还没完全展开它的左右子树。
  2. 第二次遇到(要收割):它的左右子树已经被处理(或按顺序排好了),现在轮到把它加入最终结果里了。

null 标记的作用,相当于一张"免死金牌"或"收割通行证"。 我们在把"当前节点(中节点)"压回栈里等待后续处理时,紧接着在它上面压入一个 null。 下次只要我们从栈里弹出一个 null,就知道:"哦,排在 null 下面的这个节点,已经走完过场了,可以直接把它的值加进结果集了!"


代码拆解:只需区分"排队"和"收割"

整个 while 循环内部,其实只有极度清晰的两大块逻辑:

java 复制代码
if (node != null) {
    // 【排队指挥官】:只负责按规定顺序把节点和孩子重新放回栈里,绝对不碰真实数据!
} else {
    // 【无脑收割机】:看到 null 标签了?拔出下一个节点,直接拿走它的值!
}
  • if 分支 负责排队部署 :弹出当前节点,然后将右节点、左节点、中节点按特定顺序重新压栈,并在中节点压栈后立马跟上一个 null
  • else 分支 负责真正收割 :一旦遇到 null,说明栈里的顺序已经排好了,立刻弹掉 null,接着弹出底下真正等待输出的节点,加入结果集(result.add)。

压栈顺序

既然明白了框架,那前序、中序、后序的区别到底在哪? 答案是:只有压栈的排队顺序不同。

因为栈是 "后进先出" 的数据结构,所以你希望节点按什么顺序被拿出 ,压栈时就必须反过来压 !并且记住一个铁律:null 永远紧紧贴在需要收割的"中节点"后面压入!

1. 前序遍历(期望结果:中 -> 左 -> 右)

反向操作,压栈顺序必须是:右 -> 左 -> 中

java 复制代码
// 前序遍历的排队逻辑
stack.pop(); 
if (node.right != null) stack.push(node.right);  // 压 右
if (node.left != null) stack.push(node.left);    // 压 左
stack.push(node);                                // 压 中
stack.push(null);                                // 紧跟标记!

2. 中序遍历(期望结果:左 -> 中 -> 右)

反向操作,压栈顺序必须是:右 -> 中 -> 左

java 复制代码
// 中序遍历的排队逻辑
stack.pop(); 
if (node.right != null) stack.push(node.right);  // 压 右
stack.push(node);                                // 压 中
stack.push(null);                                // 紧跟标记!
if (node.left != null) stack.push(node.left);    // 压 左

3. 后序遍历(期望结果:左 -> 右 -> 中)

反向操作,压栈顺序必须是:中 -> 右 -> 左

java 复制代码
// 后序遍历的排队逻辑
stack.pop(); 
stack.push(node);                                // 压 中
stack.push(null);                                // 紧跟标记!
if (node.right != null) stack.push(node.right);  // 压 右
if (node.left != null) stack.push(node.left);    // 压 左

完整源码(以中序遍历为例)

感受一下这套模板的整洁与优美:

java 复制代码
public List<Integer> inorderUnified(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    if (root != null) stack.push(root);
    
    while (!stack.isEmpty()) {
        TreeNode node = stack.peek(); // 先看一眼栈顶是普通节点还是 null
        
        if (node != null) {
            // 【排队指挥官】
            stack.pop(); // 弹出该节点,避免重复操作
            if (node.right != null) stack.push(node.right);  // 右
            stack.push(node);                                // 中
            stack.push(null);                                // 贴上标记
            if (node.left != null) stack.push(node.left);    // 左
        } else {
            // 【无脑收割机】
            stack.pop();            // 弹出 null 标记
            node = stack.pop();     // 取出下面那个带着标记的真实节点
            result.add(node.val);   // 收割!加入结果集
        }
    }
    return result;
}

总结

这就是二叉树的统一迭代法:利用反向压栈理清顺序,利用 null 标记区分"排队"与"收割"

相关推荐
m0_739312879 小时前
【自动驾驶和机器人控制】万向锁(Gimbal Lock)原理详解 + 和欧拉角 / 四元数 / SO(3)对比
算法·机器人·自动驾驶
白山编程大哥9 小时前
Java 迭代器接口详解:从 Iterator 到 ListIterator 的完整指南
java·开发语言·windows
热心网友俣先生9 小时前
2026国赛C题:五年命题规律与预测
java·c语言·前端·数学建模
见叶之秋9 小时前
【C++】String类的使用(含模拟实现)
java·开发语言·c++
Felven9 小时前
A. Eating Game
算法
竹叶青_七弦9 小时前
从复制粘贴到Harness:Java后端AI编程的工程化之路
java
wuminyu9 小时前
Linux内核线程调度与虚拟线程调度机制系统级深度剖析
java·linux·c语言·jvm·c++
小溪学编程10 小时前
Java InputStream 详解:从基础到实战
java·python·php
血小板要健康10 小时前
队列 + 宽搜(BFS):二叉树层序遍历 算法总结
java·数据结构·笔记·算法·leetcode·宽度优先
Felven10 小时前
A. Riptide
算法·c 算法