数据结构之树和二叉树
第七章树和二叉树
第一节树的概念
1.概念
是n(n≥0)个节点的有限集。当n=0时,称为空树。在任意一棵非空树中应满足:
- 有且仅有一个特定的称为根的结点;
- 当n>1时,其余结点可分为m(m>0)个互不相交的有限集,其中每个集合本身又是一棵树,并且成为根的子树
2.特点
- 树的根结点没有前驱,除根节点外的所有结点有且只有一个前驱;
- 树中所有结点可以由零个或多个后继
3.基本性质
\begin{flalign}
&1)树中的结点数等于所有结点的度数之和+1&&\
&2)度为m的树中第i层上至多有m^{i-1}个结点(i≥1)\
&3)高度为h的m叉树至多有(m^h-1)/(m-1)个结点\
&4)n结点m叉树最小高度为\lceil log_m[n(m-1)+1] \rceil \
\end{flalign}
4.存储结构
(1)双亲表示法
graph TB; R((R)) A((A)) B((B)) C((C)) D((D)) E((E)) F((F)) G((G)) H((H)) K((K)) R-->A R-->B R-->C A-->D A-->E C-->F F-->G F-->H F-->K
data | parent | |
---|---|---|
0 | R | -1 |
1 | A | 0 |
2 | B | 0 |
3 | C | 0 |
4 | D | 1 |
5 | E | 1 |
6 | F | 3 |
7 | G | 6 |
8 | H | 6 |
9 | K | 6 |
(2)孩子表示法
将每个节点的孩子结点都用单链表链接起来形成一个线性结构,此时n个结点就有n个孩子链表(叶子结点链表为空表);
这种存储方式寻找子女的操作非常直接,而寻找双亲的操作需要遍历n个结点中孩子链表指针域所指向的n个孩子链表。
(3)孩子兄弟表示法
优点:方便地实现树转换为二叉树的操作,易于查找结点的孩子等;
缺点:从当前结点查找其双亲结点比较麻烦
①在兄弟结点之间加一条线;
②对每个结点,只保留它与i一个孩子的连线,而与其他孩子 连线全部抹掉;
③以树根为轴心,顺时针旋转45°。
5.树、森林与二叉树遍历的对应关系
树 | 森林 | 二叉树 |
---|---|---|
先根遍历 | 先序遍历 | 先序遍历 |
后根遍历 | 中序遍历 | 中序遍历 |
第二节二叉树
1.概念和性质
(1)概念
一棵二叉树是结点的一个有限集合,该集合:
- 或者为空;
- 由一个根节点加上两棵别称为左子树和右子树的二叉树组成
满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。
完全二叉树:效率很高的数据结构,由满二叉树而引出来的。是一种特殊的完全二叉树。
二叉排序树:左<中<右.
平衡二叉树:左子树与右子树深度之差不超过1。
(2)性质
\begin{flalign}
&1)n_0 = n_2 +1\
&2)非空二叉树上第k层至多有2^{k-1}个结点(k≥1)\
&3)高度为h的二叉树至少有2^h-1个结点(h≥1)\
&4)对完全二叉树按向上到下、从左到右的顺序依次编号1,2,…,n,则有以下关系:\
&~①当i>1时,结点i的双亲的编号是\lfloor i/2 \rfloor,即当i为偶数时,其双亲的编号是i/2,它是双亲的左孩子。当i为奇数时,其双亲的编号是(i-1)/2,它是双亲的右孩子。\
&~②当2i≤n时,结点i的左孩子编号为2i,否则无左孩子。\
&~③当2i+1≤n时,结点i的右孩子编号为2i+1,否则无右孩子。\
&~④结点i所在层次(深度)为\lfloor log_2i \rfloor+1。\
&5)具有n个(n>0)结点的完全二叉树的高度为\lceil log_2(n+1) \rceil 或\lfloor log_2n \rfloor +1。
\end{flalign}
2.存储结构和基本算法
(1)顺序存储结构
(2)链式存储结构
lchild | data | rchild |
---|
1 | typedef struct BiTree{ |
第三节二叉树的遍历算法
1.先序遍历
递归算法
1 | void PreOrder(BiTree T){ |
非递归算法
1 | void PreOrder(BiTree T){ |
2.中序遍历
递归算法
1 | void InOrder(BiTree T){ |
非递归算法
1 | void InOrder(BiTree T){ |
3.后序遍历
递归算法
1 | void PostOrder(BiTree T){ |
非递归算法
1 | void PostOrder(BiTree T){ |
4.层次遍历
1 | void LevelOrder(BiTree T){ |
第四节线索二叉树
是以一定的规则将二叉树中的结点排列成一个线性序列,从而得到集中遍历序列,使得该序列中的每个结点(第一个和最后一个结点除外)都有一个直接前去和直接后继。
目的:为了加快查找结点前驱和后继的速度。
空指针 = 线索数 = n+1
lchild | ltag | data | rtag | rchild |
---|
ltag
- 0 lchild域指示结点的左孩子
- 1 lchild域指示结点的前驱
rtag
- 0 rchild域指示结点的右孩子
- 1 rchild域指示结点的前驱
存储结构
1 | typedef struct ThreadNode{ |
第五节哈夫曼树
1.概念
树中所有叶结点的带权路径长度之和称为该树的带权路径长度,记作:
$$
WPL = \sum_{i=1}^nw_il_i
$$
举例如下哈夫曼树,WPL = (4+2)3+52+7*1 = 35
graph TB; A((A)) B((B)) C((C)) D((D:7)) E((E:5)) F((F:2)) G((G:4)) A-->D A-->B B-->C B-->E C-->G C-->F
2.编码问题(左0右1)
graph TB; A((A)) B((B)) C((C)) D((D:7)) E((E:5)) F((F:2)) G((G:4)) A--1-->D A--0-->B B--0-->C B--1-->E C--0-->G C--1-->F
D | E | F | G |
---|---|---|---|
1 | 01 | 001 | 000 |