“在缓存无关模型中,设一棵每层都填满的二叉树有 $N\ge1$ 个节点、$h\ge1$ 层,每个节点占常数个存储单元。在约 $h/2$ 层切开:上半形成高度 $\lceil h/2\rceil…”
形式陈述
二叉树是一种递归结构:它可以为空,也可以由一个根节点、一个左子树和一个右子树组成,两棵子树仍是二叉树。若节点保存类型
本条目只讨论有限、无共享、无环的二叉树;不同节点可以保存相同载荷。左右是两个有名字的槽位,因此
“每个节点至多有两个孩子”还不够完整:即使只有一个孩子,也必须说明它在左边还是右边。普通有序根树若只有一个孩子,仅给孩子排序并不能记录这一区别。
非空二叉树忽略根和左右槽位后,底层无向图是一棵树。图论中的树在本条目所链接的约定下要求非空;这里允许空树,是为了让左右子树的递归定义统一成立。
根的深度为
直觉
二叉树把一个问题拆成“当前节点、左边的问题、右边的问题”。表达式
左右子树都为空的节点称为叶子。对二叉树做递归时,空树提供终止条件,非空树提供分解步骤。节点数就可以写成
这里分开的左右子树没有共享节点,否则将两边计数相加可能重复计算;带共享的 DAG 应使用另一套定义和遍历约定。结构归纳也沿着同一个分解工作:先证明空树满足性质,再假定左右子树分别满足性质,证明加上一个根后仍成立。有限性保证这种向更小子树的递归最终会遇到空树,不能把同样的终止论证直接搬到带环指针结构上。
二叉树只规定形状,并不规定载荷之间的大小。二叉搜索树另加键的有序性,二叉堆另加堆序和形状限制。不能因为画出了二叉树,就直接获得对数查找时间。
例子与边界
三种容易混淆的形状
中文“满二叉树”的用法并不完全统一,因此这里同时给出英文术语与精确定义。
| 术语 | 本条目采用的条件 | 条件关注什么 |
|---|---|---|
| full / proper(二分支树) | 每个非叶节点恰有两个非空子树 | 不允许只有一个孩子 |
| perfect(完美二叉树) | 每个非叶节点有两个孩子,且全部叶子同深度 | 每层都填满 |
| complete(完全二叉树) | 除最底层外各层填满,最底层从左到右连续填入 | 按层存储时没有空洞 |
完美树同时满足另外两种条件,但反向不成立。根有左叶子和右内部节点、右内部节点再有两个叶子的五节点树是 full,却不是 complete;一个根只有左孩子的两节点树是 complete,却不是 full。平衡则讨论子树高度或其他规模差异,需要另行给出具体约束。
高度与节点数
第
左端对应尽量把层填满;右端由一条单分支链达到。因此“二叉”只限制分支数,并不阻止树退化成线性高度。沿根到叶的搜索通常花费
设
相减得到
遍历序列不一定保存完整结构
前序、中序、后序分别按“根—左—右”“左—根—右”“左—右—根”访问非空节点。对 * + a b c,后序为 a b + c *;后序适合先算子表达式再执行运算。
只有前序载荷序列一般不能恢复树。根为 a、唯一孩子为 b 的两棵树,无论 b 在左还是右,前序都为 a b。若连空槽位也用 # 记录,则两者分别为 a b # # # 与 a # b # #,结构就被区分开。
这个带空标记的编码还有直接的合法性检查。开始有一个待填的根槽位;读到 # 消耗一个槽位,读到载荷则消耗一个槽位并产生左右两个槽位,净增一个。每次读取前必须还有槽位,且输入结束时恰好归零。例如 a b # # # 的剩余槽位依次为
节点标签互异时,前序加中序可以唯一重建:前序首项给出根,中序中的根把左右节点集合分开,再递归处理。标签重复时,这个分割未必唯一。只为普通根树记录进入与退出的括号,也可能漏掉单孩子所在的左右槽位;二叉树编码必须保留相应空位信息。
推论与应用
形状决定适合的表示
完全二叉树可以紧密存入数组。使用从
递归遍历通常用调用栈保存尚未完成的分支,额外空间为
递归结构还能用于计数
忽略节点载荷,设
例如
参考资料
- Thomas H. Cormen 等,Introduction to Algorithms,第 4 版,MIT Press,2022,附录 B 与第 10、12 章:树的基本约定、表示及二叉搜索树。
- Robert Sedgewick、Kevin Wayne,Algorithms,第 4 版,Addison-Wesley,2011,§3.2 Binary Search Trees:用递归二叉结构组织查找,及树形对成本的影响。
- Donald E. Knuth,The Art of Computer Programming, Vol. 1,第 3 版,Addison-Wesley,1997,§2.3:二叉树、遍历、表示与计数。