二叉树的中序遍历 递归调用的完整展开
层序遍历为 1 2 3 4 5 6 7 的二叉树,其中序遍历的递归调用的过程实际上是什么样的?
// 中序遍历
const inorder = (root) => {if (!root) {return;}inorder(root.left);res.push(root.val);inorder(root.right);}
递归调用的完整展开
为了更清晰地看到所有执行的语句,我们可以将所有递归调用展开:
inorder(1):if (!1) → falseinorder(1.left) → inorder(2):if (!2) → falseinorder(2.left) → inorder(4):if (!4) → falseinorder(4.left) → inorder(null):if (!null) → returnres.push(4)inorder(4.right) → inorder(null):if (!null) → returnres.push(2)inorder(2.right) → inorder(5):if (!5) → falseinorder(5.left) → inorder(null):if (!null) → returnres.push(5)inorder(5.right) → inorder(null):if (!null) → returnres.push(1)inorder(1.right) → inorder(3):if (!3) → falseinorder(3.left) → inorder(6):if (!6) → falseinorder(6.left) → inorder(null):if (!null) → returnres.push(6)inorder(6.right) → inorder(null):if (!null) → returnres.push(3)inorder(3.right) → inorder(7):if (!7) → falseinorder(7.left) → inorder(null):if (!null) → returnres.push(7)inorder(7.right) → inorder(null):if (!null) → return
初始理解
首先,我们需要明确几个概念:
-
层序遍历:这是指按照树的层次从上到下、从左到右依次访问节点。给定的层序遍历序列是
1 2 3 4 5 6 7
,这意味着:- 第一层:1(根节点)
- 第二层:2(左子节点),3(右子节点)
- 第三层:4(2的左子节点),5(2的右子节点),6(3的左子节点),7(3的右子节点)
因此,二叉树的结构如下:
1/ \2 3/ \ / \4 5 6 7
-
中序遍历:这是指按照“左子树 - 根节点 - 右子树”的顺序访问节点。对于上面的二叉树,中序遍历的结果应该是
4 2 5 1 6 3 7
。 -
递归过程:给定的中序遍历的递归代码会按照上述顺序访问节点。我们需要将递归调用展开,看看具体的执行顺序。
递归展开
让我们从根节点 1
开始,逐步展开递归调用。我们将 inorder(root)
替换为具体的节点值来表示当前的 root
。
初始调用:inorder(1)
1. inorder(1)
root
是1
,不为null
,所以:inorder(1.left)
:即inorder(2)
res.push(1.val)
:即res.push(1)
inorder(1.right)
:即inorder(3)
2. inorder(2)
root
是2
,不为null
,所以:inorder(2.left)
:即inorder(4)
res.push(2.val)
:即res.push(2)
inorder(2.right)
:即inorder(5)
3. inorder(4)
root
是4
,不为null
,所以:inorder(4.left)
:4.left
是null
,inorder(null)
直接返回res.push(4.val)
:即res.push(4)
inorder(4.right)
:4.right
是null
,inorder(null)
直接返回
- 执行顺序:
inorder(null)
(4.left
):无操作res.push(4)
inorder(null)
(4.right
):无操作
inorder(4)
完成
回到 inorder(2)
- 已经执行了
inorder(4)
,接下来:res.push(2)
inorder(5)
4. inorder(5)
root
是5
,不为null
,所以:inorder(5.left)
:5.left
是null
,inorder(null)
直接返回res.push(5.val)
:即res.push(5)
inorder(5.right)
:5.right
是null
,inorder(null)
直接返回
- 执行顺序:
inorder(null)
(5.left
):无操作res.push(5)
inorder(null)
(5.right
):无操作
inorder(5)
完成
回到 inorder(2)
- 已经执行了
inorder(5)
,inorder(2)
完成
回到 inorder(1)
- 已经执行了
inorder(2)
,接下来:res.push(1)
inorder(3)
5. inorder(3)
root
是3
,不为null
,所以:inorder(3.left)
:即inorder(6)
res.push(3.val)
:即res.push(3)
inorder(3.right)
:即inorder(7)
6. inorder(6)
root
是6
,不为null
,所以:inorder(6.left)
:6.left
是null
,inorder(null)
直接返回res.push(6.val)
:即res.push(6)
inorder(6.right)
:6.right
是null
,inorder(null)
直接返回
- 执行顺序:
inorder(null)
(6.left
):无操作res.push(6)
inorder(null)
(6.right
):无操作
inorder(6)
完成
回到 inorder(3)
- 已经执行了
inorder(6)
,接下来:res.push(3)
inorder(7)
7. inorder(7)
root
是7
,不为null
,所以:inorder(7.left)
:7.left
是null
,inorder(null)
直接返回res.push(7.val)
:即res.push(7)
inorder(7.right)
:7.right
是null
,inorder(null)
直接返回
- 执行顺序:
inorder(null)
(7.left
):无操作res.push(7)
inorder(null)
(7.right
):无操作
inorder(7)
完成
回到 inorder(3)
- 已经执行了
inorder(7)
,inorder(3)
完成
回到 inorder(1)
- 已经执行了
inorder(3)
,inorder(1)
完成
执行顺序总结
按照上述展开,res.push
的执行顺序(即中序遍历的顺序)如下:
inorder(4)
:res.push(4)
inorder(2)
:res.push(2)
inorder(5)
:res.push(5)
inorder(1)
:res.push(1)
inorder(6)
:res.push(6)
inorder(3)
:res.push(3)
inorder(7)
:res.push(7)
因此,res
中的最终顺序是 [4, 2, 5, 1, 6, 3, 7]
。
实际执行的 res.push
语句顺序
从上面的展开中,提取所有 res.push
的语句:
res.push(4)
res.push(2)
res.push(5)
res.push(1)
res.push(6)
res.push(3)
res.push(7)
验证中序遍历
让我们手动进行中序遍历以验证:
- 从根节点
1
开始:- 遍历左子树
2
:- 遍历
2
的左子树4
:4
无左子节点,访问4
4
无右子节点
- 访问
2
- 遍历
2
的右子树5
:5
无左子节点,访问5
5
无右子节点
- 遍历
- 访问
1
- 遍历右子树
3
:- 遍历
3
的左子树6
:6
无左子节点,访问6
6
无右子节点
- 访问
3
- 遍历
3
的右子树7
:7
无左子节点,访问7
7
无右子节点
- 遍历
- 遍历左子树
访问顺序:4, 2, 5, 1, 6, 3, 7
,与递归展开的结果一致。
最终答案
将所有的递归调用展开后,实际执行的 res.push
语句的顺序如下:
res.push(4)
res.push(2)
res.push(5)
res.push(1)
res.push(6)
res.push(3)
res.push(7)
因此,中序遍历的结果是 [4, 2, 5, 1, 6, 3, 7]
。