数据结构与算法分析之AVL平衡树
来源:互联网 发布:ubuntu pyqt 安装 编辑:程序博客网 时间:2024/06/06 02:33
AVL树定义:
AVL(Adelson-Velskii和Landis)树是带有平衡条件的二叉查找树。平衡条件是:其每个节点的左子树和右子树的高度差最多等于1的二叉查找树。
AVL ADT操作:
Find操作:
AVL树的查找操作跟非AVL二叉查找树的查找是一样的,查找最小值沿左子树寻找,查找最大值沿右子树查找
Insert操作:
AVL树的插入操作除了新建节点并插入到树中,还要为保持树的平衡性做调整的动作。通常通过围绕不平衡的点进行旋转来保持平衡性。
对于AVL树的插入操作破坏的是从插入点到根节点路径上的节点的平衡性。因为只有这些节点的子树由于插入导致平衡性的变化。所以,在插入操作时,
需要沿着插入点到根节点的路径上更新每个节点的新高度,并检查节点左右子树的高度差。当发现节点左右子树的高度差大于等于2时,说明此节点是一个
不平衡的节点,需要通过单旋转或者双旋转来恢复树的平衡性。第一个不平衡的节点也是所有不平衡节点中最深的那个。
假设节点a是一个不平衡的节点,在节点a处出现不平衡的情形有四种:
1.对a的左儿子的左子树进行一次插入(左-左)
2.对a的左儿子的右子树进行一次插入(左-右)
3.对a的右儿子的左子树进行一次插入(右-左)
4.对a的右儿子的右子树进行一次插入(右-右)
其中情形1和4是关于a点的镜像对称,情形2和3是关于a点的镜像对称。所以,理论上可以分为两大类,一类是插入发生在"外边"的情况(即左-左或右-右),
此类情形可以通过围绕a点的单旋转来恢复平衡性;第二类是插入操作发生在内部的情形(即右-左或左-右),此类情形可以通过围绕a点的双旋转来调整。
AVL树的单旋转:
1.左-左情形的单旋转:
左左情形的单旋转又可以细分成两类,一种是a节点的左儿子没有右儿子,另一种是a节点的左儿子没有右儿子。所以编码实现时要适应这两种情况,
避免造成子树的丢失。如下图所示
左左情形中,要围绕不平衡节点(K1)和K1->Left进行旋转,当K1->Left(假定称为K2)有右儿子时,旋转前值的排序肯定是K1>K2->Right>K2。所以使K1->Left=K2->Right
来避免K2的右子树丢失,并最终返回K2的新节点。值得注意的是调整后的新节点K2与其新的父节点(上图中的5,4)是怎样保持链接的,由于插入操作的实现是递归方式的,先递归向下寻找可插入的点,插入完成后会递归返回,在返回时会检查节点的平衡性,如果不平衡做旋转操作。所以调整后新节点K2于其新的父节点的链接是通过对旋转函数的调用来实现的,如:T = SingleRotateWithLeft( T );或K3->Left = SingleRotateWithRight( K3->Left );等。左单旋转code如下所示:
static Position SingleRotateWithLeft(Position K1){Position K2;K2 = K1->Left;K1->Left = K2->Right;K2->Right = K1;K1->Height = Max(Height(K1->Left), Height(K1->Right)) + 1;K2->Height = Max(Height(K2->Left), K1->Height) + 1;//或者K2->Height = Max(Height(K2->Left), Height(K2->Right)) + 1;return K2;}
2.右右情形的单旋转:
右右情形和左左情形类似,也可以细分为两种情况,可以跟左左情况对比来看。如下图所示:
右右单旋转code如下所示:
static Position SingleRotateWithRight(Position K1){Position K2;K2 = K1->Right;K1->Right = K2->Left;K2->Left = K1;K1->Height = Max(Height(K1->Left), Height(K1->Right)) + 1;K2->Height = Max(Height(K2->Right), K1->Height) + 1;return K2;}
左右情形:
左右情形即在节点K3的左儿子的右子树上插入造成的。可以通过两步单旋转来完成所谓的双旋转操作:先将K3的左儿子进行一次右单旋转,旋转后的结果是K3左儿子的左子树更深,所以再通过一次围绕K3的左单旋转最终修复K3的不平衡性。左右情形的双旋转如下图所示:
左右双旋转的code如下所示:
static Position DoubleRotateWithLeft(Position K3){K3->Left = SingleRotateWithRight(K3->Left);return SingleRotateWithLeft(K3);}
右左情形的双旋转:
和左右情形的双旋转类似,右左情形是在节点K3的右儿子的左子树上插入造成不平衡性。也可以通过两次单旋转来完成所谓的双旋转操作:先将K3的右儿子进行一次左单旋转,然后将K3再进行一次右单旋转。右左情形的双旋转示意图如下:
右左情形的双旋转code实现如下:
static Position DoubleRotateWithRight(Position K3){K3->Right = SingleRotateWithLeft(K3->Right);return SingleRotateWithRight(K3);}
Insert操作code实现如下:
AvlTree Insert(ElementType X, AvlTree T){if (T == NULL) {/* 找到插入点 */T = malloc(sizeof(struct AvlNode));if (T == NULL) {printf("Out of space!!!\n");exit(1);} else {T->Element = X;T->Height = 0;T->Left = T->Right = NULL;}} else if (X < T->Element) {//需要在T的左子树上插入T->Left = Insert(X, T->Left);/* 插入完成返回时检查节点的平衡性 *//* 因为是在左子树上插入,所以左子树高度在前减右子树高度 */if (Height(T->Left) - Height(T->Right) == 2)if (X < T->Left->Element)T = SingleRotateWithLeft(T);//左左情形elseT = DoubleRotateWithLeft(T);//左右情形} else if (X > T->Element) {//需要在T的右子树插入T->Right = Insert(X, T->Right);if (Height(T->Right) - Height(T->Left) == 2)if (X > T->Right->Element)T = SingleRotateWithRight(T);//右右情形elseT = DoubleRotateWithRight(T);//右左情形}//更新T节点的高度T->Height = Max(Height(T->Left), Height(T->Right)) + 1;return T;//最终返回的是新的根节点}
删除操作:
节点的删除操作也会造成不平衡,根据插入操作分析的4种不平衡性,删除操作也会造成这四种不平衡性,即LL,LR,RR,RL。相应的也要通过单旋转或者双旋转来修复不平衡性。可以把删除造成的不平衡的4种情形看作插入时造成的4种不平衡情形的镜像。
删除时的策略还是使用跟普通二叉查找树一样,将删除节点的右子树中最小的那个节点替换删除节点,并递归的删除替换节点,直到替换节点只有左子树。
删除节点后,开始递归的在删除节点到根节点的路径上检查每个节点的平衡性,若不平衡则调整。4种不平衡的情形,在code处理时可分为两大类并检查。同样假设a节点不平衡。
1.删除的节点在a节点的左子树
2.删除的节点在a节点的右子树
进一步细分上面两种情形:
1.删除的节点在a节点的左子树:
此种情形会减小a节点左子树的高度,此时根据a节点的左儿子的左右子树高度的差异,可以确定删除操作对应的不平衡性是LL还是LR。
1.1如果a节点的左儿子的左子树比右子树高,则属于LL类型,只需要将a节点进行左单旋转即可。
1.2如果a节点的左儿子的左子树比右子树低,则属于LR类型,只需要将a节点进行左双旋转即可。
2.删除的节点在a节点的右子树:
此种情形会减小a节点的右子树高度,根据a节点右儿子的左右子树高度大小,可以确定删除操作造成的不平衡性属于RR还是RL。
2.1如果a节点的右儿子的左子树比右子树低,则属于RR类型,只需要将a节点进行右单旋转即可。
2.2如果a节点的右儿子的左子树比右子树高,则属于RL类型,只需要将a节点进行右双旋转即可。
无图无真相,还是上图吧,下图是左子树删除的情形
下图是右子树删除的情形:
删除操作code实现
AvlTree Delete(ElementType X, AvlTree T){AvlTree TmpCell = NULL;if (T == NULL)return NULL;else if (X < T->Element) {/* 左子树删除的情形 */T->Left = Delete(X, T->Left);/* 在左子树删除,会造成右高左低,所以右高度减去左高度 */if (Height(T->Right) - Height(T->Left) == 2) {//T节点失去平衡if (Height(T->Right->Left) > Height(T->Right->Right))DoubleRotateWithRight(T);//RL情形elseSingleRotateWithRight(T);//RR情形}} else if (X > T->Element) {/* 删除的节点在T的右子树 */T->Right = Delete(X, T->Right);/* 在右子树删除,会造成左高右低,所以左高度减右高度 */if (Height(T->Left) - Height(T->Right) == 2) {//T节点失去平衡if (Height(T->Left->Right) > Height(T->Left->Left))DoubleRotateWithLeft(T);//LR情形elseSingleRotateWithLeft(T);//LL情形}} else if (T->Left && T->Right) {//找到了要删除的节点T, 并使用T的右子树中最小的那个替换TmpCell = FindMin(T->Right);T->Element = TmpCell->Element;T->Right = Delete(T->Element, T->Right);} else {//找到最后一个替换的节点,其实该节点肯定没有左儿子AvlTree Position = T;if (T->Left)T = T->Left;if (T->Right)T = T->Right;free(Position);}if (T)T->Height = Max(Height(T->Left), Height(T->Right)) + 1;return T;}
- 数据结构与算法分析之AVL平衡树
- 数据结构与算法之AVL平衡树<六>
- 数据结构与算法10: 平衡二叉树AVL(AVL Tree)
- java数据结构与算法之平衡二叉树(AVL树)的设计与实现
- java数据结构与算法之平衡二叉树(AVL树)的设计与实现
- java数据结构与算法之平衡二叉树(AVL树)的设计与实现
- 数据结构与算法之10(AVL自平衡二叉树与RB红黑树)
- 数据结构与算法系列----平衡二叉树(AVL树)
- 数据结构与算法问题 AVL二叉平衡树
- 算法与数据结构 其六 Avl平衡树
- 数据结构与算法专题之树——平衡二叉树(AVL树)
- 数据结构与算法分析之平衡二叉树的建立
- 【数据结构】平衡二叉树之AVL树
- 数据结构之平衡二叉树AVL
- 数据结构之 AVL平衡树 (c++)
- java数据结构与算法之平衡二叉树(AVL树)的设计与实现中的事实代码
- 【数据结构与算法分析】二叉查找树与AVL树
- 数据结构与算法分析-AVL树的实现
- 逆元求法
- SpringBoot 事务的应用
- mysql中没有提交的事务查询
- Linux 显示文件的某几行
- kubernetes(3):Centos7 安装 kubernetes admin 版本
- 数据结构与算法分析之AVL平衡树
- Android启动模式及Intent属性----重新认识Android(5)
- hdu 1255 覆盖的面积 矩阵面积交
- HDU 4612 Warm up
- JavaScript简单取得函数作用域内变量的方法
- 机器学习之Kmeans
- LoadRunner-receive time
- HTML给标签的点击事件绑定匿名函数
- 三、zedboard Linux内核编译