(e2)中序遍历

官方信息技术老师·10 页·深入(追求细节与边界)·0 次浏览·3 天前
中序遍历递归与迭代边界场景复杂度分析

(e2)中序遍历

彻底搞懂中序遍历的递归与迭代,吃透边界与复杂度

按 空格/→ 演示下一步

1 / 10 页

全部页面点击任意一页,跳回舞台从这页播放

中序遍历递归与迭代边界场景复杂度分析

(e2)中序遍历

彻底搞懂中序遍历的递归与迭代,吃透边界与复杂度

1第 1 页 · (e2)中序遍历

递归

上一节我们写出了 `inorder(node.left)`——一个函数在自己体内调用自己。这凭什么合法?它又何时停下?让我们把递归拆开看。

定义
函数在自己的代码体里调用自己
两个要素
终止条件告诉你何时停,递归式把问题变小
调用栈模型
每次调用生成独立栈帧,层层压入、返回时弹出
信任递归
假设子调用能正确解决子问题,不必追到底层
典型应用
树/图遍历、阶乘、斐波那契、回溯、分治
俄罗斯套娃对应 →递归调用

每层套娃装着更小的同款,最里层实心不可拆——对应终止条件

f(n)={1,n=0nf(n1),n>0f(n) = \begin{cases} 1, & n = 0 \\ n \cdot f(n-1), & n > 0 \end{cases}
2第 2 页 · 递归

观察

上页我们用递归写出了中序遍历,但「能跑通」和「看透它」是两回事。这一页我们停下来观察——亲自跟一次遍历过程,看它到底按什么顺序访问节点,又会暴露哪些隐藏性质。

顺序:左 → 根 → 右
根节点的访问严格发生在「左子树遍历完」之后、「右子树开始之前」
BST 上得到升序序列
对二叉搜索树做中序遍历,输出恰好是从小到大的有序序列
递归栈的延迟访问
先一路压栈到最左下,再逐层「访问+弹栈」,最后才走进右子树
翻字典按拼音查字对应 →BST 上中序遍历

字典页天然按拼音升序;BST 中序天然按 key 升序——本质都是「已排好序」的产物

3第 3 页 · 观察

思路

上一页我们看到递归骨架——两次自我调用夹一次「访问」。现在把观察凝成思路:中序遍历的本质是什么、关键判断在哪、以及它最适合解决哪些问题。

访问顺序
固定为「左 → 根 → 右」, 这是中序区别于前序/后序的唯一标识
递归信任
本层只处理根节点, 左/右子树的遍历完整委托给递归调用, 假设它会做对
BST 特殊性质
BST 因左小右大, 中序遍历天然产出严格递增序列
典型应用
验证 BST、求第 k 小、对 BST 原地排序
学生按学号进教室对号入座对应 →BST 中序遍历得到升序

BST 左小右大的结构, 让中序天然排出大小序, 像按号就座

4第 4 页 · 思路

构思

观察完示例,我们已经看到访问顺序是先左后右、中间回头。现在要把这个直觉升华为正式定义,并看清它为什么这么有用。

「中」的含义
根节点的访问被夹在左、右子树遍历之间,所以叫「中序」
三段拼合结构
左子树序列 + 根 + 右子树序列,三段首尾相接构成完整访问
BST 的天然排序
对二叉搜索树做中序遍历,结果恰好按 key 升序排列
典型适用场景
有序输出、验证 BST、查找第 k 小/大元素等需要顺序访问时
会议挨个发言对应 →中序遍历

每人先听左边说完、自己再说、再听右边——自己的话正好落在中间

inorder(T)=inorder(TL)    {root}    inorder(TR)\text{inorder}(T) = \text{inorder}(T_L)\;\cup\;\{\text{root}\}\;\cup\;\text{inorder}(T_R)
5第 5 页 · 构思

实现

前面想清楚了「左、根、右」的访问思路,但要写成能跑的代码,还得把入栈时机、空指针判定这些细节钉死。这一页把递归、迭代、Morris 三种写法摊开,顺便讲最容易踩的坑。

递归写法
思路最直的翻译,代码最短,但依赖系统调用栈
迭代写法
用显式栈手动模拟递归,每一步都可追溯
Morris 遍历
借叶子节点的空右指针做线索,空间压到 O(1)
核心顺序
左子树走完才能处理根,再走右子树
典型应用
BST 升序输出、合法性校验、求第 k 小元素
走迷宫左转优先对应 →中序遍历

沿左墙一路走到底、走不通回头才处理路口(节点)、再探右岔路

6第 6 页 · 实现

实例

代码已经写好,但单看 inorder 三行还是会绕——下面用一棵具体的树,手动展开递归调用栈,看中序遍历到底按什么顺序访问节点。

具体树形
7 节点 BST:根 4,左 2、1、3,右 6、5、7,严格左小右大
递归顺序
递归路径固定为左子树访问完 → 根 → 右子树访问完
输出序列
1 → 2 → 3 → 4 → 5 → 6 → 7,等价于升序数组
典型应用
BST 的有序输出;表达式树还原为中缀表达式
字典按拼音升序排列对应 →BST 的中序遍历

中序天然按「左小右大」访问,输出就是已排好的字典

7第 7 页 · 实例

分摊分析

储值卡 1000 元喝 50 杯咖啡——看似贵,分摊到每杯 20 元且事先能算清。这就是分摊思想:不看一次性大开销,看连续操作每次的稳定代价。Morris 遍历正是用它证明整体线性。

分摊分析
看连续操作的总开销是否有上界,而非单次最坏情况
与平均分析的区别
分摊是确定性「保证」,平均是概率意义上的「期望」
核心思路
偶尔的贵操作被大量便宜操作分摊,单次分摊代价是常数
Morris 遍历中的应用
每个前驱线索最多建立+断开各一次,n 个节点总代价 O(n)
储值卡分摊到每杯咖啡对应 →分摊分析

一次投入 1000 元,50 次用完,每次确定花 20 元,不是「可能」

8第 8 页 · 分摊分析

本节要点

  • 中序遍历即「左→根→右」的递归定义,不依赖具体实现
  • 递归与显式栈完全等价,时空复杂度同为 O(n)
  • Morris 遍历借叶子空指针把辅助空间压到 O(1)
  • 对 BST,中序结果即为升序,是验证 BST 的天然工具
延伸主题:前序与后序遍历的对比与变体Morris 遍历的指针线索化用中序遍历验证 BST 合法性
9第 9 页 · 本节要点

课后思考

先凭直觉作答,再对照参考答案;想不出来比答错更值得停下来。

1为什么对二叉搜索树做中序遍历,输出的节点值恰好是有序的?

参考答案中序按左→根→右访问。BST 定义左子树都小于根、右子树都大于根,因此所有节点恰好按从小到大被访问一次。这是结构定义与访问顺序的天然契合。

2如果二叉树不是搜索树,普通二叉树的中序遍历还有什么用?

参考答案仍能完整访问每个节点一次。适用于表达式语法树求值、文件系统目录展开等任何'先处理完左侧、再处理根、再处理右侧'的场景。

3允许重复键时 BST 的中序遍历还能保证有序吗?需要怎么调整?

参考答案仍能完整访问所有节点,但需要约定相等键放左还是放右;不同约定会得到不同序列,调整关键是统一相等键的处理规则。

10第 10 页 · 课后思考