数据结构与算法碎片积累
Posted 鸿_H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法碎片积累相关的知识,希望对你有一定的参考价值。
前言:继续愉快周末下午肝blog,搞完去跑步。
1、平衡二叉树
平衡二叉树,要么其是一棵空树,要么其的左、右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1
1)平衡二叉排序树:
标准案例。
这个满足平衡二叉排序树性质,所以是一棵平衡二叉树。
2)非平衡二叉树例子:
这里不是平衡二叉排序树,因为10大于3,应该在右侧却在左侧;1小于9,应该在左侧却在右侧,不满足二叉排序树性质。
这不属于平衡二叉树,因为不满足左右的深度差不大于1(对于9点,其右子树深度为0,左子树深度为2,深度差为2,大于1)
2、平衡二叉树的实现原理
1)将二叉树的结点上的左子树的深度的值减去右子树的深度的值成为平衡因子BF(balance factor),平衡二叉树就是一棵二叉树上所有结点的平衡因子的绝对值小于等于1的树
2)平衡二叉树构建过程中,检查平衡性,如果出现破坏情况,立刻调整。调整方式就是,旋转最小不平衡子树。(实现原理)
这里最小不平衡子树就是包含结点9在内的的左子树。
2)最小不平衡子树旋转例子(从一个数组构建一个平衡二叉树)
2-1)出现最小不平衡子树,旋转3结点得到如下
2-2)
2-3)插入结点5后,出现最小不平衡子树情况,如下
2-4)将结点3左旋转,2连接4结点,得到如下
2-5)插入结点6后,结点2的左右子树深度差大于1
2-6)将2结点左旋转,将结点3连接到结点2的右子树上【这个操作是一个性质,具体忘了。需要温习前面课程笔记blog了】,得如下
2-7)插入结点7,结点5出现最小不平衡子树,如图
2-8)结点5左转,,结点6连接到结点4上
2-9)插入结点10
2-10)插入结点9,结点4、6、7出现不平衡情况,如图
2-11)先将结点9,10旋转,得到
2-12)将结点7绕结点9逆时针旋转,将结点9连接到结点6上,如图
2-13)插入结点8,结点6,4的深度差都是大于1的,如图
2-14)先将结点7顺时针(绕结点9)旋转,并连接到结点6上【结点8应该连接到结点9的左子树的,但是这里忘记做了】,如
2-15)将结点6绕结点7逆时针旋转
2-16)将结点8连接到结点9的左子树上【这是上面结点7旋转时,结点8应该放到结点9的左子树的】
数组转平衡二叉树,在出现不平衡点时,及时调整;调整需要找到关键结点,然后确定顺时针还是逆时针旋转方向即可。【这里的旋转方向貌似跟左右之差的正负相关,但是还不懂。】
3、平衡二叉树的实现原理(代码实现)
代码没看懂,左平衡、右平衡、左旋,右旋,子树增高这些没搞明白。老师说,这个代码原理就是上面的平衡二叉树构建的过程。
//210204
#define LH 1 //表示左子树长高了
#define EH 0 //表示左右子树等高
#define RH - 1 //表示右子树长高
#define TRUE 1
#define FALSE 0
#include <string>
typedef struct BiTNode {
int data;
int bf;
struct BiTNode* lchild, * rchild;
}BiTNode,*BiTree;
void R_Rotate(BiTree* p) { //右旋操作
BiTree L;
L = (*p)->lchild;
(*p)->lchild = L->rchild;
L->rchild = (*p);
*p = L;
}
void L_Rotate(BiTree* p) { //左旋操作
BiTree L;
L = (*p)->rchild;
(*p)->rchild = L->lchild;
L->lchild = (*p);
*p = L;
}
void LeftBalance(BiTree *T) { //左平衡
BiTree L, Lr;
L = (*T)->lchild;
switch (L->bf)//
{
case LH:
(*T)->bf = L->bf = EH;
R_Rotate(T); //右旋转操作
break;
case RH:
Lr = L->rchild;
switch (Lr->bf)
{
case LH:
(*T)->bf = RH;
L->bf = EH;
break;
case EH:
(*T)->bf = L->bf = EH;
break;
case RH:
(*T)->bf = EH;
L->bf = LH;
break;
default:
break;
}
Lr->bf = EH;
L_Rotate(&(*T)->lchild); //左旋转操作
R_Rotate(T); //右旋转操作
break;
default:
break;
}
}
//右平衡老师没写,只是参考
void RightBalance(BiTree* T) { //右平衡
BiTree L, Lr;
L = (*T)->rchild;
switch (L->bf)//
{
case RH:
(*T)->bf = L->bf = EH;
R_Rotate(T); //右旋转操作
break;
case LH:
Lr = L->rchild;
switch (Lr->bf)
{
case RH:
(*T)->bf = RH;
L->bf = EH;
break;
case EH:
(*T)->bf = L->bf = EH;
break;
case LH:
(*T)->bf = EH;
L->bf = LH;
break;
default:
break;
}
Lr->bf = EH;
L_Rotate(&(*T)->lchild); //左旋转操作
R_Rotate(T); //右旋转操作
break;
default:
break;
}
}
int InsertAVL(BiTree *T,int e,int *taller) { //*taller用于检测是否长高了,长高了表示不平衡
if (!*T) { //找不到就新建结点
*T = (BiTree)malloc(sizeof(BiTNode));
(*T)->data = e;
(*T)->lchild = (*T)->rchild = NULL;
(*T)->bf = EH;
*taller = TRUE;
}
//
else
{
if (e == (*T)->data) {
*taller = FALSE;
return FALSE; //表示查找失败
}
if (e < (*T)->data) {
if (!InsertAVL(&(*T)->lchild, e, taller))
{
return FALSE;
}
if (*taller) {
switch ((*T)->bf)
{
case LH://表示左子树长高了
LeftBalance(T);
*taller = FALSE;
break;
case EH://原是左右子树等高的,现在因为左子树增高了
(*T)->bf = LH;
*taller = TRUE;
break;
case RH:
(*T)->bf = EH;
*taller = FALSE;
break;
default:
break;
}
}
}
else //插入的是右孩子部分
{
if (!InsertAVL(&(*T)->rchild, e, taller)) {
return FALSE;
}
if (*taller) {
switch ((*T)->bf)
{
case LH://表示左子树长高了
(*T)->bf = EH;
*taller = FALSE;
break;
case EH://原是左右子树等高的,现在因为左子树增高了
(*T)->bf = RH;
*taller = TRUE;
break;
case RH:
RightBalance(T);
*taller = FALSE;
break;
default:
break;
}
}
}
}
}
//只知道这是上一节课的原理代码实现,什么左旋右旋这些东西。内心一脸懵逼,没懂。
有空时候少打游戏,回来琢磨琢磨,消磨一下时间。
4、多路查找树之2-3树
1)多路查找树(multi-way search tree)
2)多路查找树的特点是其每一个结点的孩子数可以多于两个,且每个结点处可以存储多个元素
3)多路查找树的所有元素之间存在着某种特定的排序关系
4)多路查找树中的每一个结点都具有两个孩子或者三个孩子,称之为2-3树。
5)一个结点拥有两个孩子和一个元素,称之为2结点,其和二叉排序树类似,左子树包含的元素小于结点的元素,右子树包含的元素大于结点的元素。但,与二叉排序树不同的是,这2结点要么没有孩子,要么就有两个孩子,不存在只有一个孩子情况。
6)2-3树要求所有的叶子在同一个层次上;并且多路查找树也是一个排序树,同一层次,从左到右满足一定从小到大的顺序;或者说,使用中序遍历,获取一个从小到大的排序结果。
5、多路查找树之2-3树的插入原理
1)首先,对现有的插入结点块进行拓展;当现在子树到根块层次都是3结点块情形,那么这时候就往下开辟一层。
2)当需要插入结点2时,发现上述的没法拆了,而且直达根块8,12,都是3结点了,只能开辟新的一层才行。开辟新的层数,同样满足叶子同一层次原则,所以全体拆,直到满足为止。
3)2-3树插入情形:
如果是空树,直接插入2-3作为根结点块;
否则,插入到二结点块中,使之成为一个三结点块;
非上述两种情况,若双亲属于2结点块,则通过扩展双亲直3结点块来插入;
若追溯到根结点块都是属于三结点块情形,那只能通过增加层数来插入;增加层数时,从底层开始调整,其他的子树根据满足叶子同一层次原则进行调整。
6、多路查找树之2-3树的删除原理
1)当待删除结点位于叶子的三结点块情况,直接将三结点块变为二结点块情况;
2)当待删除结点位于叶子的二结点块情况,双亲结点块也属于二结点块,另一个子树属于三结点块情况时,为了保证多路查找树必须是有双孩子原则,通过旋转来实现,如图
3)当待删除结点位于叶子的二结点块情况,双亲结点块也属于二结点块,另一个子树属于二结点块情况时,通过从其他树中的三结点块中调整进行删除,如图
4)当删除结点位于叶子,叶子属于二结点块,其双亲属于三结点块,如删除上图的结点10,删除操作就是,将双亲结点块分拆到叶子结点中,如图下
5)
当删除的结点属于满二叉树的叶子情况,如图上,删除操作就是减少层数,如删除结点8,则有如图:
6)当待删除结点不属于叶子结点情况,而是属于双亲结点情况,初图示:
6-1)如需要删除结点4,由于待删除点属于双亲结点,同时双亲结点属于二结点块,其有一个三结点块的孩子,通过将三结点块调整为二结点块,移动一结点代替待删除点位置,如图下
6-2)若删除的双亲结点属于三结点块,孩子中存在三结点块情况,如删除上图中的结点12,将孩子结点中的三结点块中一个结点移动到待删除结点位置,如图下:
7、多路查找树之2-3-4树和B树
1)依次插入结点,注意结点块中结点块增加,结点块的合并以及分拆,最终得到如下树
2)删除结点,注意四结点块变三结点块甚至二结点块;若删除的是结点属于双亲结点时,注意将结点合并、分拆
3) 图上删除结点3,则将结点2,4合并成三结点块,如图下
4)B树(B-tree)是一种平衡的多路查找树,2-3树和2-3-4树都是B树的特例
5)把结点最大的孩子树数目称为B树的阶(order),因此,2-3树是3阶B树,2-3-4树是4阶B树
6)一个m阶的B树具有如下属性:
----如果根结点不是叶子点,则其至少有两棵子树
----每一个非根的分支结点都是k-1个元素(关键字)和k个孩子,其中k满足:[m/2]<=k<=m
【[m/2]由于输入法局限原因,这里表示向上取整数意思】
----所有叶子节点都是位于同一层次
----每一个分支结点包含下列信息数据:
其中K为关键字,且Ki<Ki+1
Ai为指向子树根结点的指针
7)B树例子
8)B树结构用于磁盘数据存取。
这些笔记是2021年2月份的,现在十月份,回来再看,忘记差不多了,哈哈哈。任重道远!
#########################
不积硅步,无以至千里
好记性不如烂笔头
感谢小甲鱼老师
截图权利归原作者所有
以上是关于数据结构与算法碎片积累的主要内容,如果未能解决你的问题,请参考以下文章