4. 树与二叉树
4.1 树的基本概念
树的结点数:N>=0,N=0时为空树
树适用于表示有层次结构的数据。
N个结点的树有N-1条边:
树中结点的最大度数称为树的度
度>0的点称为分支节点,度=0的点称为叶子节点(终端节点)
结点层次:根节点为1
树的高度(深度)是树中结点的最大层数
有序树:结点的子树从左到右是有次序的,反之为无序树
两个结点的路径是由这两个结点之间所经过的结点序列构成的
路径长度是路径上所经过的边的个数
树的路径长度 是从根结点到每一结点路径长度的总和(不同于边的数量)
树中的相关性质(度为m):
第i层:至多个结点
(重点) 按序号从上到下,每层从左到右编号满m叉树:
则:第 i 层节点数:
结点 i 的第 k 个孩子编号为: (前i-1个节点都有了m个孩子,再加上自己)
结点 i 的双亲编号为:
| m叉树 | 至少 | 至多 |
|---|---|---|
| 高度h->结点n | ||
| 结点n->高度h |
注意:结点数=边+1
任何一个树中的叶子节点数量为:
n个结点构造的不同的树的数量:
4.2 二叉树的概念
4.2.1 二叉树的定义及主要特性
二叉树的度>=2 二叉树可以是空树 二叉树是有序树
对比度为2的有序树:
度为2的有序树不可以为空,且最小结点数为3
二叉树的结点次序不是相对另一结点而言,是确定的
满二叉树:
根为i:左孩子为2i,右孩子为2i+1
完全二叉树:
或者
| 完全二叉树 | 至少 | 至多 |
|---|---|---|
| 高度h->结点n | ||
| 第 i 层有 k 个叶子结点->结点n |
二叉树性质:
第k层至多
n个结点构造的不同的二叉树的数量:
| 二叉树 | 至少 | 至多 |
|---|---|---|
| 高度h->结点n | ||
| 结点n->高度h | 或 |
n个结点的二叉树有n+1个空链域
4.2.2 二叉树的存储结构
顺序存储结构:将编号为i的节点元素存储到数组下标为i-1的分量中
完全二叉树和满二叉树适合用来存储。
链式存储结构:分为数据域,左指针域和右指针域。
4.3 二叉树的遍历和线索二叉树
4.3.1 二叉树的遍历
遍历:先序遍历,中序遍历,后序遍历
三种遍历方式的时间复杂度都是O(n),空间复杂度都是O(n).
三种遍历方法中叶子节点的遍历先后顺序都是固定的。
可以用后序遍历找到祖先的路径
前序序列和中序序列的关系是以前序序列为入栈次序,中序序列为出栈顺序(卡特兰数)
| 形态 | 相同 | 相反 |
|---|---|---|
| 先序,后序 | 只有根 | |
| 先序,中序 | ||
| 中序,后序 | ||
| 先序,层次 | 只有根或空树 | |
| 中序,层次 | ||
| 后序,层次 |
ssr 二叉树的非递归遍历(见习题)。
二叉树的层次遍历(同图的广度遍历),需要借助队列。
由遍历序列构造二叉树:(见习题)
1.先序序列+中序序列
2.后序序列+中序序列
3.层次序列+中序序列
前序序列和后序序列不能唯一确定二叉树,但是可以确定节点的祖先关系,前序序列为XY与后序序列为YX时(两者相邻!),X为Y的祖先。
4.3.2 线索二叉树
1.概念
线索二叉树是个物理结构
实质就是对一个非线性结构进行线性化操作
每个线索二叉树都有n+1个线索(每个树都有n+1个空指针)
线索化后的空的链域为:
| 线索化后空链域数 | 1 | 2 |
|---|---|---|
| 先序线索树 | 根左子树不为空 | 根左子树为空 |
| 中序线索树 | 不存在 | = |
| 后序线索树 | 根右子树不为空 | 根右子树为空 |
引入线索二叉树是为了加快查找结点前驱和后继的速度。
二叉树线索化时:若无左子树(ltag=1),令lchild指向前驱节点。
若无右子树(rtag=1),令rchild指向前驱节点。
带头节点的线索二叉树:
线索化后空的链域为0
头节点左孩子为根节点,右孩子为最后访问的节点
序列的第一个节点的左孩子和最后一个节点的右孩子指向头节点
中序线索树的前驱后继容易求出(左孩子最右边,右孩子最左边)。
先序线索树的后继容易求出,但是前驱必须知道双亲(栈存取),前序线索树的遍历不需要栈的支持。
后序线索树的后继不能求出,必须知道双亲(栈存取),后序线索树的遍历需要栈的支持(一般)。
注:并非所有后序线索树遍历都要用栈,比如任何结点最多只有左子树的后序线索树就不需要。
不是每个节点都可以通过线索找到它的前驱和后继,先序线索树的前驱必须知道双亲,后序线索树的后继必须知道双亲。
4.4 树,森林
4.4.1 树的存储结构
1.双亲表示法
采用一组连续空间来存储每个结点(类数组),伪指针域为**-1(无双亲)或者指向双亲结点下标**。
可以很快得到每个结点的双亲结点,但是求孩子结点需要遍历整个结构。
2.孩子表示法(手绘)
将每个结点的孩子结点都用单链表链接起来。
找子女非常直接,寻找双亲需要遍历N个节点中孩子链表指针域所指向的N个孩子链表。
3.孩子兄弟表示法(手绘)
又称二叉树表示法
二叉链表作为存储结构 |指向第一个孩子结点的指针|结点值|下一个兄弟结点的指针
方便实现树转二叉树的操作,易于查找孩子。
注:森林中有n个结点b条边,则森林中共有n-b棵树。
4.4.2 树,森林与二叉树的转换
左孩子右兄弟。
森林F->二叉树B:
*B中无右孩子结点数=F中非叶节点数+1
*F中叶结点数=B中无左孩子结点数
4.4.3 树和森林的遍历
树的遍历:
先根遍历:先访问根结点,再依次先根遍历子树。
后根遍历:顺序后根遍历子树,再访问根节点。
森林的遍历:
先序遍历:1.访问森林中第一棵树的根节点
2.先序遍历第一棵树中根节点的子树森林
3.先序遍历除去第一棵树之后剩余的树构成的森林
*中序遍历:1.中序遍历森林中第一棵树的根节点的子树森林
2.访问第一棵树的根节点
3.中序遍历除去第一棵树之后剩余的树构成的森林
| 树 | 森林 | 二叉树 |
|---|---|---|
| 先根遍历 | 先序遍历 | 先序遍历 |
| 后根遍历 | 中序遍历 | 中序遍历 |
| 森林(树) F | 二叉树 B |
|---|---|
其中 ,
代表只有左子树的节点个数,
代表只有右子树的节点个数。
4.5 树的应用
4.5.1 二叉排序树
特性:左子树结点值<根结点值<右子树结点值(递归定义)
对二叉排序树进行中序遍历可以获得递增的有序序列
插入:插入的结点一定是叶子结点。
删除: 1.如果删除的是叶子节点,则直接删除,不会改变性质。
2.若结点只有一个子树,则将该子树挂载到该节点父结点下,删除该结点。
3.若结点z有两个子树,则将z的直接后继(或前驱)替代z,然后从二叉排序树中删去这个直接后继(前驱),此过程递归的进行。
时间复杂度:当二叉排序树只有单枝树时,时间复杂度为O(n).
平均时间复杂度为O(logn) (二叉排序树深度为)
二叉排序树查找序列:最后找到的为X,则前面的序列元素中比X小的递增,比X大的递减。
4.5.2 平衡二叉树
平衡因子:左子树高度-右子树高度
平衡二叉树:每个节点平衡因子绝对值小于2的二叉排序树。
最少结点(最大深度)(所有非叶子结点平衡因子都是1):
插入:未导致不平衡则直接插入
若不平衡进行旋转(距离插入点最近的不平衡点为A)
LL平衡旋转(右单旋转):由于在A的左孩子的左子树上插入
将A的左子树B取代A,同时A成为B的右子树,B的右子树成为A的左子树。
RR平衡旋转(左单旋转):由于在A的右孩子的右子树上插入
将A的右子树B取代A,同时A成为B的左子树,B的左子树成为A的右子树
LR平衡旋转(先左后右双旋转):由于在A的左孩子的右子树上插入
先对A的左孩子进行RR平衡旋转,再对A进行LL旋转。
RL平衡旋转(先右后左双旋转):由于在A的右孩子的左子树上插入
先对A的右孩子进行LL平衡旋转,再对A进行RR旋转。
平均查找长度:
4.5.3 哈夫曼树 哈夫曼编码
WPL :树的带权路径长度是指树中的所有的叶子节点的带权路径的长度之和
哈夫曼树:WPL最小的二叉树
哈夫曼树的特点
权值越大的节点 离根节点越近
树中没有度为1的节点
每个值都会成为叶子结点(叶子结点数为前缀编码数)
叶子结点数N,非叶子结点N-1,总结点数2N-1
哈夫曼编码
若没有一个编码是另一个编码的前缀,则称为前缀编码
根通往任一叶子节点的路径都不可能是通往其余叶子节点路径的子路径
0表示转向左孩子,1表示转向右孩子
哈夫曼树构造
1)构造一个新结点,挑出两个权值最小的构成,同时讲新结点值设置为和
2)重复直到只有一个
哈夫曼树的构造不唯一
哈夫曼N叉树
构造方法同二叉树类似
度为m的哈夫曼树中,叶子节点个数为n,则非叶子节点的个数为