Skip to content

5.1 树

5.1.1 树的定义

Pasted image 20251109182350.png

5.1.2 基本术语

路径是有向边 Pasted image 20251109182755.png

Pasted image 20251109183049.png

有序数与无序树

Pasted image 20251109183153.png

森林

Pasted image 20251109183250.png

5.1.3 树的性质

除了根结点,每个结点都有一个入度 Pasted image 20251109183415.png

Pasted image 20251109183625.png

Pasted image 20251109183736.pngPasted image 20251109183826.pngPasted image 20251109183906.png

Pasted image 20251109184142.pngPasted image 20251109184206.png

5.2 二叉树

5.2.1 基本概念

Pasted image 20251109184434.png

满二叉树与完全二叉树

Pasted image 20251109184905.png

二叉排序树

Pasted image 20251109185116.png

平衡二叉树

Pasted image 20251109185231.png

5.2.2 二叉树的性质

Pasted image 20251109185556.png

Pasted image 20251109185612.png

Pasted image 20251109185701.png

Pasted image 20251109185910.pngPasted image 20251109190029.png

Pasted image 20251109190234.png

5.2.3 二叉树的存储结构

顺序存储

Pasted image 20251109190604.png

Pasted image 20251109190727.png 使用顺序存储二叉树来存储,只适合完全二叉树,会有大量空位: Pasted image 20251109191037.png

链式存储

n个结点,有n-1个指针指向结点(根结点没有),每个结点有两个指针域,共有2n个,剩余的n+1个指针域为null: Pasted image 20251109191235.png

代码实现: Pasted image 20251109191628.png

5.3 二叉树的遍历

5.3.1 先中后序遍历(深度优先)

Pasted image 20251109192415.pngPasted image 20251109192650.png

代码实现

调整visit()的位置就可以实现不同的算法: Pasted image 20251109192802.png

先序是第一次访问的结点,中序是第二次访问的结点: Pasted image 20251109193230.png

求深度

Pasted image 20251109193539.png

5.3.2 层次遍历(广度优先)

Pasted image 20251109193800.png

代码实现

Pasted image 20251109194027.png 保存指针即可,可以减小空间消耗

5.3.3 遍历序列构造二叉树

只依靠一种遍历序列不可能构造出唯一的二叉树结构,至少两种且必须包括中序:

Pasted image 20251109195252.png

先序与中序

Pasted image 20251109195701.pngPasted image 20251109195748.pngPasted image 20251109195811.png

后序与中序

原理与先序一样

层次与中序

Pasted image 20251109200220.png

5.3.4 线索二叉树

在普通的二叉树中去通过一个非根结点去遍历或寻找它的前驱与后继是很困难的

中序线索二叉树

Pasted image 20251109201735.png 寻找中序后继next: Pasted image 20251111165126.png

寻找中序前驱pre: Pasted image 20251111165412.png

先序线索二叉树

寻找先序后继next: Pasted image 20251111170026.png

寻找先序前驱pre: Pasted image 20251111170343.png

  • 如果使用三叉链表(有指向父节点的指针) Pasted image 20251111170656.png

后序线索二叉树

后序前驱: Pasted image 20251111171315.png 后序后继: Pasted image 20251111171712.pngPasted image 20251111171615.png 总结上述三种: Pasted image 20251111171805.png

存储结构

Pasted image 20251109202007.png

Pasted image 20251109202033.png

代码实现

中序线索化: Pasted image 20251109203009.png

另一种实现方式: Pasted image 20251109204300.png 前序和后序调整visit()的代码顺序即可,但是注意在先序线索化的时候要判断某个结点的左孩子是否为线索化的前驱,这样会有循环问题,后序没有这个问题。 Pasted image 20251109203809.png 无论哪种线索化,都要注意最后一个结点的右孩子和rtag的处理

5.4 树的结构

5.4.1 树的存储结构

双亲表示法(顺序存储)

树中每个非跟结点都有一个根结点。找父节点方便,但是找孩子结点困难,需要遍历数组。同时这种方式可以表示森林 Pasted image 20251111172554.png

孩子表示法(顺序+链式存储)

便于找孩子,找父节点困难。可以表示森林,但要记录多个跟结点。 Pasted image 20251111173043.png

孩子兄弟表示法(链式存储)

也可以表示森林,森林中每棵树视为兄弟关系 Pasted image 20251111174130.png

5.4.2 树、森林二叉树之间的转换

树、森林转化为二叉树

使用孩子兄弟表示法 Pasted image 20251111174900.png

Pasted image 20251111175013.png

二叉树转化为树、森林

树: Pasted image 20251111175533.png

森林: Pasted image 20251111175655.png

5.4.3 树与森林的遍历

树的遍历

先根(深度): Pasted image 20251111181605.png

后根(深度): Pasted image 20251111181749.png

层次(深度): Pasted image 20251111182000.png

森林的遍历

先序:等同于对每棵子树做先根遍历,也等同于对森林转化的二叉树的先序遍历 Pasted image 20251111182240.png

中序:等同于对每棵子树做后根遍历,也等同于对森林转化的二叉树的中序遍历 Pasted image 20251111182615.png

5.5 树的应用

5.5.1 哈夫曼树

带权路径长度(WPL)最小的二叉树,可能不唯一 Pasted image 20251111182949.png

Pasted image 20251111183102.pngPasted image 20251111183030.png

构造哈夫曼树

Pasted image 20251111183310.pngPasted image 20251111183351.png

哈夫曼编码(可变长编码)

对于使用固定长度编码的树,所传递的信息的效率较低,也就是WPL较大,可以使用哈夫曼树来优化(编码结果可能不唯一),并且解码不会有歧义(前缀编码): Pasted image 20251111184403.png

5.2.1 并查集

在集合这种逻辑结构中,两个元素之间的关系是从属于同一集合或从属于不同集合。使用树来表示一个集合,双亲表示法(顺序存储)更适合实现并查集 Pasted image 20251111190344.png

代码实现

Pasted image 20251111190638.png

性能分析

时间复杂度:Find:O(n) ,Union:O(1) 对于合并集合(树)的过程,要尽可能的树“矮胖”(小树合并到大树中),以在Find的过程过程中尽可能减少访问结点的次数: Pasted image 20251111191242.png

算法优化

优化Union: Pasted image 20251111192819.png

优化Find: Pasted image 20251111193557.png