题目描述
给定一个二叉树的根节点 root,返回它的中序遍历结果。
中序遍历的访问顺序为:左子树 → 根节点 → 右子树。
解题思路
二叉树的中序遍历是算法面试中的基础题,通常有两种主流的解法:递归法和迭代法。
方法一:递归法(最直观)
递归是二叉树最自然的遍历方式。我们将大问题拆解为小问题:
- 递归遍历当前节点的左子树。
- 访问(记录)当前节点的值。
- 递归遍历当前节点的右子树。
- 终止条件:当节点为
null时,直接返回。
方法二:迭代法(使用栈模拟)
如果不允许使用递归,我们需要手动维护一个栈(Stack) 来模拟系统底层的函数调用栈。
核心思想是 “一路向左,撞墙回头” :
- 准备一个指针
current指向根节点。 - 一路向左:只要
current不为空,就将当前节点压入栈中,并让current指向它的左子节点。 - 撞墙回头:当
current为空时,说明左子树已经到底了。此时从栈顶弹出一个节点,访问该节点的值。 - 转向右子树:将
current指向刚刚弹出节点的右子节点,准备开启下一轮循环。 - 循环条件:只要
current不为空,或者栈中还有未处理的节点,就继续循环。
JavaScript 代码实现
1. 递归实现
var inorderTraversal = function(root) {
const res = [];
const dfs = (node) => {
if (!node) return; // 递归终止条件
dfs(node.left); // 遍历左子树
res.push(node.val);// 访问根节点
dfs(node.right); // 遍历右子树
};
dfs(root);
return res;
};
2. 迭代实现(推荐掌握)
var inorderTraversal = function(root) {
const res = [];
const stack = [];
let current = root;
// 当当前节点不为空,或者栈不为空时,继续遍历
while (current !== null || stack.length > 0) {
// 1. 一路向左,将沿途节点全部压入栈中
while (current !== null) {
stack.push(current);
current = current.left;
}
// 2. 左边走到底了,弹出栈顶节点(即最左侧的节点)
current = stack.pop();
// 3. 访问该节点
res.push(current.val);
// 4. 转向该节点的右子树
current = current.right;
}
return res;
};
复杂度分析
-
时间复杂度: O(n)O(n) ,其中 nn 是二叉树的节点数。每个节点都会被精确地访问一次。
-
空间复杂度: O(h)O(h) ,其中 hh 是二叉树的高度。
- 递归法消耗的是系统调用栈的空间。
- 迭代法消耗的是我们手动创建的栈的空间。
- 在最坏情况下(树退化为链表),空间复杂度为 O(n)O(n) ;在最好情况下(完全平衡二叉树),空间复杂度为 O(logn)O(logn) 。