树(理论篇)
树(理论篇)
设n为总结点数,ni为出度为i的结点数,h为树高
树的基本概念
-
树的度:树中结点的最大度数
-
结点的度:结点的孩子数量
-
分支数即边数
-
根结点处,树高h=1
-
n=n0+n1+n2+……ni+1
- 该公式可用以求ni,如:求叶结点数即求n0的大小
- 结合二叉树的n0=n2+1,可以求更多关于ni的问题
-
**总边数=1 *n1+2 *n2+3 *n3……i *ni **
-
n=总边数+1
-
二叉树:每个结点至多只有两子树的可为空的有序树
-
满二叉树:只有n2和n0两种结点,树中每层都有最多的结点
-
完全二叉树:最后一层以上是一颗满二叉树,最后一层右边缺少部分叶结点
-
若n为奇数:则只有n0和n2的结点,若n为偶数:有且只有一个n1的结点
-
可作为有序表的折半查找判定树
- ASL=log2(n+1)-1
- 表长为n的有序表折半查找的判定树高为log2(n+1)向上取整
- 查找成功的ASL:第i层的i*该层的圆形非叶节点个数的求和
- 查找失败的ASL:第i层的i*该层的方形非叶节点个数的求和
- 判定树的最小分支高度即最少比较次数,最大分支高度即最多比较次数
- 折半查找判定树是一棵完全二叉树
-
作为一种特殊的二叉排序树,ASL=O(log2n)
-
-
二叉排序树:任一结点都有左值>根结点值>右值(也叫二叉搜索树)
-
查找,插入,构造操作:详见实践篇
-
删除操作:分为三种情况
- 被删结点z为叶节点:直接删
- 被删结点z只有左孩子或右孩子:删掉结点,左右孩子补位
- 被删结点z有左孩子和右孩子:将z的左孩子放到z右孩子最左面结点的左孩子处,转为情况二
-
查找成功的ASL:第i层的i*该层的结点个数的求和
-
最坏的情况当二叉排序树是一个只有左孩子或右孩子的单支树,ASL=O(n)
-
完全二叉搜索树平均比较次数为:O(logn)
-
当有序表是动态查找表(即表中元素会变动),应选择二叉排序树作为逻辑结构
-
-
平衡二叉树:AVL树,即任一结点左右子树深度之差不超过1的二叉排序树
-
平衡因子BF:左子树与右子树的高度差,平衡二叉树的平衡因子只可能是-1,0,1
-
最小不平衡树:离插入结点最近且BF>1的结点为根节点的子树,调整操作只需要针对它调整即可
-
插入操作:前半部分与二叉排序树相同,如果这个操作造成了不平衡则需要以下调整操作
- LL右旋:让2成为根结点,3成为2的右孩子,若2本身有右孩子就将2右孩子变为3的左孩子
- RR左旋:让2成为根结点,1成为2的左孩子,若2本身有左孩子就将2左孩子变为1的右孩子
- LR先左后右:先对1-2做RR左旋,转化成LL型后做LL右旋
- RL先右后左:先对3-2做LL右旋,转化成RR型后做RR左旋
-
删除操作:前半部分与二叉排序树相同,然后向上回溯找到第一个不平衡的结点,做以上四种调整操作,若调整结束后树高减一,则需要再次向上回溯寻找有没有不平衡的祖先结点
-
结点递推公式:n0=0,n1=1,n2=2,nh=nh-1+nh-2+1(nh为构造高度为h的平衡二叉树所需的最小结点数,即树中非叶结点的平衡因子均为1)
-
-
红黑树:满足红黑性质的二叉排序树
-
红黑性质
- 结点为红色,或黑色
- 根节点为黑色
- 叶结点为黑色,且叶结点是虚构的外部NULL结点
- 不存在两个相邻的红色结点
- 对任一结点,从该结点到任一叶结点的简单路径上,所含黑结点数量相同
-
结论
- 从根到叶结点的最长路径不大于最短路径的2倍
- 有n个内部结点的红黑树的高度<=2log2(n+1)
- 查找、插入、删除的时间复杂度和AVL树相同
- 任一结点左右子树高度差不超过2倍
- 新插入红黑树的结点初始着为红色
- 属于自平衡的二叉树
-
插入:前半部分和二叉排序树相同,然后再做染色和旋转的调整操作,设插入节点为x
-
父亲结点为祖父结点的左儿子时
-
x的父结点和叔结点都是红色
- 将父结点和叔结点改成黑色
- 将祖父结点改成红色
- 最后将祖父结点视为新的x结点,递归向上调整
-
x的叔结点为黑色,x为父结点的左儿子
- 将父结点变为黑色
- 将祖父结点变为红色
- 对祖父结点进行右旋
-
x的叔结点为黑色,x为父结点的右儿子
- 对父亲-x进行左旋,让父亲变成新的x结点
- 变成情况二(x结点为父亲结点的左儿子)
-
-
父亲结点为祖父结点的右儿子时
-
x的父结点和叔结点都是红色
- 将父结点和叔结点改成黑色
- 将祖父结点改成红色
- 最后将祖父结点视为新的x结点,递归向上调整
-
x的叔结点为黑色,x为父结点的右儿子
- 将父结点变为黑色
- 将祖父结点变为红色
- 对祖父结点进行左旋
-
x的叔结点为黑色,x为父结点的左儿子
- 对父亲-x进行右旋,让父亲变成新的x结点
- 变成情况二(x结点为父亲结点的右儿子)
-
-
规律总结
- 父结点为红色时无需调整
- 叔父结点同色时,只染色不旋转
- 叔父结点不同色时,染色后如果x与叔结点同侧,先对父亲-x朝另一侧旋转,然后让祖父结点往叔结点一侧旋转
- 结束后将根结点染为黑色
-
-
删除:前半部分和二叉排序树相同,然后做调整操作,设被删除结点为x
- 规律总结
- 兄弟为红色:兄弟和父结点换色,对父结点朝x方向旋转,与x同侧的侄子成为新兄弟
- 兄弟为黑色
- 左右侄子为黑色:兄弟变为红色,父结点变为新的x结点,递归进行操作
- 与x同侧的侄子为红色:兄弟变为红色,该侄子变为黑色,对兄弟结点朝x另一侧旋转,与x同侧的侄子成为新兄弟
- 与x异侧的侄子为红色:兄弟变为父亲结点颜色,父结点和该侄子变为黑色,对父结点朝x方向旋转
- 规律总结
-
-
-
二叉树的性质
-
非空二叉树有:n0=n2+1
-
非空二叉树有:第k层至多有2(k-1)个结点
-
高为h的二叉树至多有2(k-1)个结点(等比数列求和:n=20+21+……2(h-1))
-
完全二叉树高度为:log2(n+1) 向上取整 或 log2(n)+1 向下取整
-
二叉树的顺序存储:总是会占据2h-1的空间,即使存储的不是满二叉树
-
-
二叉树的遍历
-
先序遍历:中—左—右
-
中序遍历:左—中—右
-
后序遍历:左—右—中
-
层序遍历:从上到下从左到右
- 以该图为例,先序遍历为:abdecfg,中序遍历为:dbeafcg,后序遍历为:debfgca
-
线索二叉树:如果二叉树某节点左孩子为空,就在左孩子处放前驱结点,如果右孩子为空,就在右孩子处放后继节点,前驱和后继结点具体是什么依赖于三种遍历,对应的就有三种线索二叉树
-
-
树和森林
-
存储:数组,邻接表,二叉链表
-
转化
- 树转化为二叉树:每个节点左指针指向左孩子,右指针指向相邻的右兄弟,左孩子右兄弟
- 森林转化为二叉树:每棵树都转化为二叉树,然后把第二棵树根视为第一棵的右兄弟,以次类推
- 二叉树转化为森林:不断断开当前二叉树根节点的右链,直到最后只剩没有右子树的二叉树为止
-
遍历
-
树的先根遍历 对应 森林的先序遍历 对应 二叉树的先序遍历
-
树的后根遍历 对应 森林的中序遍历 对应 二叉树的中序遍历
-
-
-
树和二叉树的应用
-
哈夫曼树
-
WPL=树带权路径长度=所有叶节点的带权路径长度之和,哈夫曼树:WPL最小的二叉树
-
构造:从n个节点的集合中选择权值最小的两个节点a和b,作为左右孩子建树为c,c的权值为a权值+b权值,然后把a和b从集合删去,加入c,重复操作
-
n个符号构成哈夫曼树,将新建n-1个结点
-
一棵哈夫曼树中只有度为0和度为m的结点
-
哈夫曼编码
-
前缀编码:可被唯一解释,它不是任何编码的前缀,也没有任何编码是它的前缀
-
哈夫曼树转为哈夫曼编码:字符作为节点,出现次数作为权值来建一棵哈夫曼树
-
哈夫曼树的叶子节点数 = 哈夫曼编码的可码字符数
-
-
-
并查集:用以查询两个节点是否在同一个集合中,双亲表示法存储的树,详见 树的应用 文件夹
-
-
本章常考理论题
- 树的度、结点数、最大高度/最小高度,知二求一的问题:画一画题目要求的树,即可求解,背公式麻烦
- n=n0+n1+…ni + 二叉树n0=n2+1 可解决很多求叶结点数,与ni相关的计算题
- 完全二叉树的最多结点数问题
- 利用n=n0+n1…+ni 以及 n0=n2+1,最后取n1=1得最大结点数
- 将完全二叉树最后一层以上的部分视为满二叉树,运用满二叉树结点公式n=2h-1,再补上最后一层的结点数,或将完全二叉树完全视为满二叉树,再减去最后一层缺少的结点数
- 二叉树四种遍历方式的相关问题
- 前序序列与后序序列相反的二叉树特点:左子树或右子树为空,即每层只有一个节点
- 前序序列和中序序列的关系相当于:以前序序列入栈,得到的出栈序列就是中序序列
- 其他问题:根据四种遍历方式的定义画画图就可以了
- 二叉排序树相关问题
- 查找成功的ASL:第i层的i*该层的结点个数的求和
- 查找的效率和树的深度有关
- 判断一个序列可不可能是二叉排序树的查找路径:根据序列把排序树画出来,如果不满足排序树定义则不可能,排序树定义(对任意结点来说,结点值总是大于左树所有元素,总是小于右树所有元素)
- 二叉平衡树相关问题
- 掌握好调整操作,根据题意把图画出来基本就可以解决
- 给出一段序列构造出二叉平衡树的问题可以用实践篇中构造平衡搜索树的方式:数组排序,数组中间作为分割点,每次分割点即是当前结点的值,左右递归往下进行
- 套用结点递推公式求解问题
- 哈夫曼编码相关问题
- 掌握好构造操作,根据题意把图画出来
- 哈夫曼树叶子结点数=可编码字符数,根据n=n0+n2,n0=n2+1可解得n0 大小,即叶子结点数
- 前缀码问题:
- 如:{0,100,110,1100}不是一个前缀编码,因为110可以变成1100的前缀,不可被唯一解释
- 字符集{a,b,c,d}的哈夫曼编码以此为0100,10,0000,0101,则01000101100000的译码为:直接用前缀码切割编码序列得0100 0101 10 0000——所得译码结果为:adbc
- 求字符集中各个元素的哈夫曼编码,step1:建立一棵哈夫曼树,step2:向左走为0向右走为1,从根结点走到各个字符得到的数字即为各个字符的哈夫曼编码
- 并查集相关问题
- find(x):返回x所在集合的根节点
- union(find(x),find(y)):将x所在集合和y所在集合合并
- 路径压缩,即每个元素的find(x)返回的总是它最根处的根节点
- 集合数组S[i]中的存储:S[i]存储的是i这个元素的父节点,当i是根节点时,S[i]= -n,其中n为i这个集合的元素个数
- 根据题意用树状图表示集合,画图求解即可
本文作者:pinoky
本文链接:https://www.cnblogs.com/pinoky/p/16914620.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步