二叉树遍历是所有树结构题目的基础,其中**中序遍历(Inorder Traversal)**尤为重要。
很多二叉搜索树(BST)相关题目,本质都离不开中序遍历。
这篇笔记将从「是什么」「怎么做」「为什么这样做」三个角度,系统梳理中序遍历的两种经典解法:
- 递归解法(最直观)
- 迭代解法(用栈模拟递归)
一、什么是中序遍历?
中序遍历的访问顺序是:
左子树 → 根节点 → 右子树
举个例子:
2
/ \
1 3
中序遍历结果为:
[1, 2, 3]
正是从小到大的顺序,这也是为什么 二叉搜索树的中序遍历一定是有序的。
二、题目描述(简化版)
给你一棵二叉树 root,请返回它的中序遍历结果。
三、解法一:递归(最直观)
思路分析
递归解法本质就是直接按照中序遍历的定义来写:
- 遍历左子树
- 访问当前节点
- 遍历右子树
这是树结构最自然、最容易理解的写法。
完整代码(递归)
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
helper(root, result);
return result;
}
public void helper(TreeNode root, List<Integer> result) {
if (root == null) {
return;
}
helper(root.left, result);
result.add(root.val);
helper(root.right, result);
}
}
逐行解释
if (root == null) {
return;
}
递归终止条件,遇到空节点直接返回。
helper(root.left, result);
先递归处理左子树。
result.add(root.val);
左子树处理完之后,再访问当前节点。
helper(root.right, result);
最后递归处理右子树。
递归的优缺点
优点:
- 代码简洁
- 逻辑清晰
- 非常符合中序遍历的定义
缺点:
- 依赖系统递归栈
- 在极端情况下(树退化成链表)可能栈溢出
四、解法二:迭代(用栈模拟递归)
递归虽然好理解,但面试和工程中经常会要求写非递归版本。
那问题来了:
递归是怎么实现中序遍历的?
答案是:靠函数调用栈。
所以,非递归版本的核心思想就是:
用一个显式的栈,去模拟递归过程。
核心思路
- 一路向左,把节点不断压栈
- 左子树走到底后,弹出栈顶节点访问
- 再转向右子树
- 重复上述过程
完整代码(迭代 + 栈)
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()) {
if (cur != null) {
stack.push(cur);
cur = cur.left;
} else {
cur = stack.pop();
result.add(cur.val);
cur = cur.right;
}
}
return result;
}
}
逐步执行过程说明
假设当前节点是 cur:
- 只要
cur不为空,就一直往左走,并压栈 - 当
cur为空,说明左子树走到头了 - 从栈中弹出一个节点,访问它
- 然后转向它的右子树
这个过程和递归的执行顺序是完全一致的。
五、递归 vs 迭代,对比总结
| 维度 | 递归 | 迭代(栈) |
|---|---|---|
| 思路 | 直观 | 稍微抽象 |
| 代码量 | 少 | 略多 |
| 栈来源 | 系统调用栈 | 手动维护栈 |
| 面试友好度 | 中 | 高 |
实际刷题建议:
- 初学阶段:优先写递归,理解遍历本质
- 熟练之后:必须掌握栈实现,这是高频考点