1. 概述
AVL树是最早提出的自平衡二叉树,在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。AVL树得名于它的发明者G.M. Adelson-Velsky和E.M. Landis。AVL树种查找、插入和删除在平均和最坏情况下都是O(log n),增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。本文介绍了AVL树的设计思想和基本操作。
2. 基本术语
有四种种情况可能导致二叉查找树不平衡,分别为:
(1)LL:插入一个新节点到根节点的左子树(Left)的左子树(Left),导致根节点的平衡因子由1变为2
(2)RR:插入一个新节点到根节点的右子树(Right)的右子树(Right),导致根节点的平衡因子由-1变为-2
(3)LR:插入一个新节点到根节点的左子树(Left)的右子树(Right),导致根节点的平衡因子由1变为2
(4)RL:插入一个新节点到根节点的右子树(Right)的左子树(Left),导致根节点的平衡因子由-1变为-2
针对四种种情况可能导致的不平衡,可以通过旋转使之变平衡。有两种基本的旋转:
(1)左旋转:将根节点旋转到(根节点的)右孩子的左孩子位置
(2)右旋转:将根节点旋转到(根节点的)左孩子的右孩子位置
3. AVL树的旋转操作
AVL树的基本操作是旋转,有四种旋转方式,分别为:左旋转,右旋转,左右旋转(先左后右),右左旋转(先右后左),实际上,这四种旋转操作两两对称,因而也可以说成两类旋转操作。
3.1 LL
LL情况需要右旋解决,如下图所示:
右旋部分代码如下:
BiTree SingleRight(BiTree k1)//单旋:右旋
{
BiTree k2;
k2 = k1->lchild;
k1->lchild = k2->rchild;
k2->rchild = k1;
k1->height = MAX(Height(k1->lchild), Height(k1->rchild));
k2->height = MAX(Height(k2->lchild), Height(k1));
return k2;
}
这是没有删除的代码,没有父母节点,看起来简单些,不要过多考虑边界
3.2 RR
RR情况需要左旋解决,如下图所示:
左旋部分代码如下:
BiTree SingleLeft(BiTree k1)//单旋:左旋
{
BiTree k2;
k2 = k1->rchild;
k1->rchild = k2->lchild;
k2->lchild = k1;
k1->height = MAX(Height(k1->lchild), Height(k1->rchild));
k2->height = MAX(Height(k2->rchild), Height(k1));
return k2;
}
3.3 LR
LR情况需要左右(先B左旋转,后A右旋转)旋解决,如下图所示:
BiTree LeftRight(BiTree k1)
{
k1->lchild = SingleLeft(k1->lchild);
return SingleRight(k1);
}
3.4 RL
RL情况需要右左旋解决(先B右旋转,后A左旋转),如下图所示:
BiTree RightLeft(BiTree k1)//双旋: 右左(从下往上 右旋-左旋)
{
k1->rchild = SingleRight(k1->rchild);//右旋
return SingleLeft(k1);//左旋
}
4. AVL数的插入和删除操作
(1) 插入操作:实际上就是在不同情况下采用不同的旋转方式调整整棵树
在查找二叉树的插入的通识判断是否出现了不平衡,若是不平衡,则根据以上4中情况做相应的调整
(2) 删除操作:首先定位要删除的节点,将该节点删除(与查找二叉树一般)
然后沿着被删除的节点自下而上的调整树到根节点(需要回溯,所以要引入父母节点)
Note:
!!!由于引入了父母节点,所以在操作的时候,旋转,删除,插入都需要注意对父母节点的变化做处理
!!!!有着更为简便的删除------懒惰删除
#include
#include
#include
#include
typedef struct tree
{
char data;
int hight;
int bf; //平衡因子:左树高度减去右树高度
struct tree *lchild;
struct tree *rchild;
struct tree *parent; //删除所用,指向父母
}Node, *Tree;
void Init(Tree *T, char data)//初始化根节点:父母为NULL
{
*T = (Tree)malloc(sizeof(Node));
(*T)->data = data;
(*T)->hight = 1;
(*T)->bf = 0;
(*T)->parent = (*T)->lchild = (*T)->rchild = NULL;
}
int MAX(int a, int b)
{
if (a > b)
return a + 1;
return b + 1;
}
int Height(Tree T)//返回高度
{
if (T)
return T->hight;
return -1;
}
Tree SingleLeft(Tree k1)//单旋:左旋 注意父母的变化
{ //引入如父母这一指针,所以需要注意在旋转的时候,需要将父母也变换
Tree k2 = k1->rchild;
k1->rchild = k2->lchild;
if (k2->lchild)
k2->lchild->parent = k1;
k2->lchild = k1;
k2->parent = k1->parent;//注意这二者的顺序,若是相反,则会有k2->parent = k2;
k1->parent = k2;
k1->hight = MAX(Height(k1->lchild), Height(k1->rchild));
k2->hight = MAX(Height(k2->lchild), Height(k2->rchild));
k1->bf = Height(k1->lchild) - Height(k1->rchild);
k2->bf = Height(k2->lchild) - Height(k2->rchild);
return k2;
}
Tree SingleRight(Tree k1)//单旋:右旋
{
Tree k2 = k1->lchild;
k1->lchild = k2->rchild;
if (k2->rchild)
k2->rchild->parent = k1;
k2->rchild = k1;
k2->parent = k1->parent;
k1->parent = k2;
k1->hight = MAX(Height(k1->lchild), Height(k1->rchild));
k2->hight = MAX(Height(k2->lchild), Height(k2->rchild));
k1->bf = Height(k1->lchild) - Height(k1->rchild);
k2->bf = Height(k2->lchild) - Height(k2->rchild);
return k2;
}
Tree LeftRight(Tree k1)//双旋:左旋——右旋
{
k1->lchild = SingleLeft(k1->lchild);
return SingleRight(k1);
}
Tree RightLeft(Tree k1)//双旋: 右旋--左旋
{
k1->rchild = SingleRight(k1->rchild);
return SingleLeft(k1);
}
Tree FindMin(Tree T)//找出最小节点
{
//错误写法如下:这样会造成对原本数据的改变,我们要做的只是找到最小的元素而已
/*
while (T->lchild)
T = T->lchild;
return T;
*/
Tree temp = T;
while (temp->lchild)
temp = temp->lchild;
return temp;
}
Tree Insert(Tree T, Tree pre, char data)//插入,边插入边旋转调整平衡 四种对应关系
{ //由于有parent,所以需要引入pre指向前驱指针
if (!T)
{
T = (Tree)malloc(sizeof(Node));
if (!T)
{
printf("Can Not Init");
return NULL;
}
T->hight = 1;
T->bf = 0;
T->data = data;
T->parent = pre;
T->lchild = T->rchild = NULL;
}
else
{
if (T->data < data)
{
T->rchild = Insert(T->rchild, T, data);
if (T->bf == -2)
{
if (data < T->rchild->data)//RL
T = RightLeft(T);
else if (data > T->rchild->data)//RR
T = SingleLeft(T);
}
}
if (T->data > data)
{
T->lchild = Insert(T->lchild, T, data);
if (T->bf == 2)
{
if (data < T->lchild->data)//LL
T = SingleRight(T);
else if (data > T->lchild->data)//LR
T = LeftRight(T);
}
}
}
T->hight = MAX(Height(T->lchild), Height(T->rchild));//高度和平衡因子的更新
T->bf = Height(T->lchild) - Height(T->rchild);
return T;
}
Tree Delete(Tree T, char data)//删除操作,比较复杂
{ //先按照一般的查找二叉树将节点删除后,再由该节点自下而上的回溯,调整每个节点到平衡至根节点
Tree temp, target = T, parent = NULL, grand;
char ele;
while (target)//删除操作
{
if (target->data < data)
target = target->rchild;
else if (target->data > data)
target = target->lchild;
else if (target->lchild && target->rchild)//找出删除节点
{//两个孩子情况
temp = FindMin(target->rchild);
parent = temp->parent;//注意该语句的顺序,若是对target做了处理且target就是temp的父母,那么此时parent并不准确
target->data = temp->data;
//if (temp->rchild)//最小节点必然没有左子树
//判断删除节点是其父母的左(右)子树,分别处理;
if (temp->parent->data <= temp->data)//右子树:注意等号,由于此时temp->parent可能正是target,故可能等于
{
temp->parent->rchild = temp->rchild;
if (temp->rchild)//注意指针父母的变化
temp->rchild->parent = temp->parent;
}
else //左子树
{
temp->parent->lchild = temp->rchild;
if (temp->rchild)
temp->rchild->parent = temp->parent;
}
free(temp);
break;
}
else//只有一个孩子或者没有孩子(树叶)
{
temp = target;
parent = temp->parent;
if (target->lchild)//有左孩子
if (parent->data < data)//判断删除节点位置在父母节点的左还是右
{
parent->rchild = target->lchild;
target->lchild->parent = parent;
}
else
{
parent->lchild = target->lchild;
target->lchild->parent = parent;
}
else //不用判断右孩子是否为空:若是为空,就是孩子,此时等于target->rchild也就是等于NULL
if (parent->data < data)
{
parent->rchild = target->rchild;
if (target->rchild)
target->rchild->parent = parent;
}
else
{
parent->lchild = target->rchild;
if (target->rchild)
target->rchild->parent = parent;
}
free(temp);
break;
}
}
while (parent)//由删除节点往上调整平衡
{
ele = parent->data;
parent->hight = MAX(Height(parent->lchild), Height(parent->rchild));//更新高度和平衡因子
parent->bf = Height(parent->lchild) - Height(parent->rchild);
grand = parent->parent;
if (parent->bf == -2)//四种对应情况,相对于插入来说,有着特殊情况,即bf==0的情况
{
if (parent->rchild->bf > 0)
{
parent = RightLeft(parent);
}
else
{
parent = SingleLeft(parent);
}
}
else if (parent->bf == 2)
{
if (parent->lchild->bf < 0)
parent = LeftRight(parent);
else
parent = SingleRight(parent);
}
if (grand)//祖父母:这是必须的,否则无法连接
{
if (grand->data > ele)//判断位置
grand->lchild = parent;
else
grand->rchild = parent;
parent->parent = grand;
}
else
T = parent;//注意:此时说明已经到了根节点,需要变化
parent = parent->parent;//往上走
}
return T;
}
int main()
{
Tree T;
int i;
char *s="ckbfjlaegmdh";
Init(&T,'i');
for (i = 0; i < strlen(s); i++)
T = Insert(T, T, s[i]);
T = Delete(T, 'a');
//T = Delete(T, 'a');
//T = Delete(T, 'j');
//T = Delete(T, 'b');
//T = Delete(T, 'b');
return 0;
}
/*
* AVL树 :每个节点的左子树和右子树的高度相差至多为1
* :左旋 root旋转到root->right->left
* : 右旋 root旋转到root->left->right
*
* 左左: 右旋
* 右右: 左旋
* 右左: 先右旋再左旋 (从下往上)
* 左右: 先左旋再右旋
*/
#include
#include
#include
#include
#define MAX(a, b) ((((a) > (b)) ? (a) : (b)) + 1)
typedef struct tree
{
int data;
int height;
struct tree *lchild;
struct tree *rchild;
}BiNode, *BiTree;
void Inisit(BiTree *T, int c)
{
*T = (BiTree)malloc(sizeof(BiNode));
(*T)->data = c;
(*T)->rchild = (*T)->lchild = NULL;
}
int Height(BiTree T)//返回树的高度, 树为空的时候返回-1
{
if (T)
return T->height;
return -1;
}
BiTree SingleLeft(BiTree k1)//单旋:左旋
{
BiTree k2;
k2 = k1->rchild;
k1->rchild = k2->lchild;
k2->lchild = k1;
k1->height = MAX(Height(k1->lchild), Height(k1->rchild));
k2->height = MAX(Height(k2->rchild), Height(k1));
return k2;
}
BiTree SingleRight(BiTree k1)//单旋: 右旋
{
BiTree k2;
k2 = k1->lchild;
k1->lchild = k2->rchild;
k2->rchild = k1;
k1->height = MAX(Height(k1->lchild), Height(k1->rchild));
k2->height = MAX(Height(k2->lchild), Height(k1));
return k2;
}
BiTree RightLeft(BiTree k1)//双旋: 右左(从下往上 右旋-左旋)
{
k1->rchild = SingleRight(k1->rchild);//右旋
return SingleLeft(k1);//左旋
}
BiTree LeftRight(BiTree k1)
{
k1->lchild = SingleLeft(k1->lchild);
return SingleRight(k1);
}
BiTree Insert(BiTree T, int c)
{
if (!T)
{
T = (BiTree)malloc(sizeof(BiNode));
if (!T)
{
printf("ERROR");
return NULL;
}
T->lchild = T->rchild = NULL;
T->data = c;
}
if (c > T->data)//Right
{
T->rchild = Insert(T->rchild, c);
if (Height(T->rchild) - Height(T->lchild) == 2)
if (c > T->rchild->data)//Right-Right
T = SingleLeft(T);
else
T = RightLeft(T);//Right-Left
}
else if (c < T->data)
{
T->lchild = Insert(T->lchild, c);
if (Height(T->lchild) - Height(T->rchild) == 2)
if (c < T->lchild->data)//Left-Left
//T = SingleRight(T->lchild->data)
T = SingleRight(T);
else
T = LeftRight(T);//Left-Right
}
T->height = MAX(Height(T->lchild), Height(T->rchild));//对高度做调整
return T;
}
int main()
{
BiTree T;
int i;
int s[]={2, 6, 1, 3, 5, 7, 16, 15, 14, 13, 12, 11, 8, 9, 10};
Inisit(&T, 4);
for (i = 0; i < 15; i++)
T = Insert(T, s[i]);
return 0;
}