一、概念
AVL树:高度平衡的二叉搜索树,共有继承了BST树,有BST树的全部特性。
AVL树的左孩子、右孩子树都是AVL树,并且左子树,右子树的高度差(右子树高度 - 左子树高度)不超过1
高度差:结点的平衡因子。如图,该树各个节点的平衡因子,有超过1,所以这不是一个AVL树。
二、AVL树的作用:
对于一般的二叉搜索树(Binary Search Tree),其期望高度(即为一棵平衡树时)为log2n,其各操作的时间复杂度(O(log2n))同时也由此而决定。但是,在某些极端的情况下(如在插入的序列是有序的时),二叉搜索树将退化成近似链或链,此时,其操作的时间复杂度将退化成线性的,即O(n)。我们可以通过随机化建立二叉搜索树来尽量的避免这种情况,但是在进行了多次的操作之后,由于在删除时,我们总是选择将待删除节点的后继代替它本身,这样就会造成总是右边的节点数目减少,以至于树向左偏沉。这同时也会造成树的平衡性受到破坏,提高它的操作的时间复杂度。
二、分析及实现
1.定义:
typedef int KeyType;//关键码
typedef struct AVLNode //树节点:设计时带有头结点:
{
AVLNode* leftchild; //左孩子
AVLNode* prarent; //双亲指针 BST、AVL、RB中都有双亲指针,因为需要回溯
AVLNode* rightchild;//右孩子
int balance; //平衡因子:-1 0 1
KeyType key; //值
}AVLNode;
typedef struct //AVL 树 头节点:有头结点则不需要二级指针来改变指针的指向(左旋右旋)
{
AVLNode* head; //树的头结点
int cursize; //结点个数
}AVLTree;
AVLNode* Buynode(AVLNode* parent=NULL)
{
AVLNode* s = (AVLNode*)malloc(sizeof(AVLNode));
if(s == NULL) exit(1);
memset(s,0,sizeof(*s));
s->prarent = parent;
return s;
}
void Freenode(AVLNode* p)
{
free(p);
}
void InitAVTree(AVLTree& myt) //初始化一棵树
{
myt.head = Buynode();
myt.cursize = 0;
}
2.插入,单旋转:
在AVL树中,插入时,造成不平衡,所以需要:旋转(左旋、右旋)
左旋:右边的数据多,造成不平衡了,所以就左旋。如图要插入120.造成了树的不平衡。45节点的balance为2.
右旋:树左边的几点多,造成不平衡,所以就右旋一下,将左边的结点向右旋转过来。
进行左旋:
函数:
void RotateLeft(AVLNode*head,AVLNode*ptr)//ptr要旋转的结点 head头结点
ptr指向45结点,head为树的头结点。
分3步骤:
第一步:当前树(45为小树根结点的这颗树)的根节点重新设定,设定为45结点的右孩子。即不平衡结点的右孩子设定为新的根节点。
第二步:根节点的右孩子要 指向 新根的左孩子
第三步:新根的左孩子指针 指向原来的根
三句代码:
//左旋
void RotateLeft(AVLNode*head,AVLNode*ptr) //3个指向改变 3个双亲改变:ptr要旋转的结点 head头结点
{
AVLNode* newroot = ptr->rightchild;
ptr->rightchild = newroot->leftchild;
newroot->leftchild = ptr;
//下面if else解决ptr是否为树的根节点问题
if(ptr->prarent == head) //ptr的双亲 是 树的头结点,即ptr为树的根节点
{
head = newroot;//那么头结点 要指向新的根
}
else//ptr的双亲 不是 树的头结点,即ptr不是树的根节点
{
if(ptr->prarent->leftchild == ptr) //ptr是其双亲的左孩子
{
ptr->prarent->leftchild = newroot;//新的根
}
else //ptr是其双亲的右孩子
{
ptr->prarent->rightchild = newroot;
}
}
}
但是parent的指向还未改变,所以还需要对parent指针域改变。如图中紫色圆圈标记,45,78,89 双亲改变。
接下来解决newroot的双亲改变,原来的根(即图中的45结点)的双亲改变,newroot的左孩子(图中78结点)的双亲改变。
紫色箭头为指向双亲。
要注意一个特殊情况:newroot的左孩子为空
//左旋
void RotateLeft(AVLNode*head,AVLNode*ptr) //3个指向改变 3个双亲改变:ptr要旋转的结点 head头结点
{
AVLNode* newroot = ptr->rightchild;//第一个指向
newroot->prarent = ptr->prarent; //1.newroot的双亲改变
ptr->rightchild = newroot->leftchild;
if(newroot->leftchild != NULL)
{
newroot->leftchild->prarent = ptr; //2.改变newroot的左孩子的双亲
}
newroot->leftchild = ptr;//2.第二个指向
//下面if else解决ptr是否为树的根节点问题
if(ptr->prarent == head) //ptr的双亲 是 树的头结点,即ptr为树的根节点
{
head = newroot;//那么头结点 要指向新的根
}
else//ptr的双亲 不是 树的头结点,即ptr不是树的根节点
{
if(ptr->prarent->leftchild == ptr) //ptr是其双亲的左孩子
{
ptr->prarent->leftchild = newroot;//新的根
}
else //ptr是其双亲的右孩子
{
ptr->prarent->rightchild = newroot;
}
}
ptr->prarent = newroot;//3.ptr的双亲 指向 newroot
}
右旋:和左旋同理
//右旋
void RotateRight(AVLNode*head,AVLNode*ptr)
{
AVLNode* newroot = ptr->leftchild;//1.新的结点
newroot->prarent = ptr->prarent;
ptr->leftchild = newroot->rightchild;//
if(newroot->rightchild != NULL)
{
newroot->rightchild->prarent = ptr;//newroot 左孩子双亲改变
}
newroot->rightchild = ptr;//3.第三个
if(ptr->prarent == head)
{
head->prarent = newroot;
}
else
{
if(ptr->prarent->leftchild == ptr)
{
ptr->prarent->leftchild = newroot;
}
else
{
ptr->prarent->rightchild = newroot;
}
}
ptr->prarent = newroot;//3,双亲
}
3.双旋转:两种:先左后右,先右后左
先看一个简单例子:什么情况下要双旋转:
void LeftBalance(AVLTree& myt,AVLNode* ptr)//左平衡
{
AVLNode* rightsub = NULL;
AVLNode* leftsub = ptr->rightchild;
switch(leftsub->balance)
{
case 0:cout<<" right already balance"<<endl;break;
case -1:
ptr->balance = 0;
leftsub->balance = 0;
RotateRight(myt.head,ptr);
break;
case 1:
rightsub = leftsub->rightchild;
switch(rightsub->balance)
{
case 0:break;
case 1:break;
case -1:break;
}
RotateLeft(myt.head,leftsub);
RotateRight(myt.head,ptr);
break;
}
}
void RightBalance(AVLTree& myt,AVLNode* ptr)//右平衡
{
AVLNode* rightsub = ptr->rightchild;//右孩子结点
AVLNode* leftsub = NULL;//左孩子结点
switch(rightsub->balance)
{
case 0:cout<<" right already balance"<<endl;break;
case 1:
ptr->balance = 0;
rightsub->balance = 0;
RotateLeft(myt.head,ptr);
break;
case -1:
leftsub = rightsub->leftchild;
switch(leftsub->balance)
{
case 0:break;
case 1:
ptr->balance = -1;
rightsub->balance = 0;
break;
case -1:
ptr->balance = 0;
rightsub->balance = 1;
break;
}
leftsub->balance = 0;
RotateRight(myt.head,rightsub);
RotateLeft(myt.head,ptr);
break;
}
}
void AdjustTree(AVLTree& myt,AVLNode* ptr)//调整函数
{
AVLNode* pa = ptr->prarent;
for(;;)//条件退出
{
if(pa->leftchild == ptr)
{
switch(pa->balance)
{
case 0:pa->balance = -1;break;
case 1:pa->balance = 0;break;
case -1:LeftBalance(myt,pa);
break;
}
}
else
{
switch(pa->balance)
{
case 0:pa->balance = 1;break;
case -1:pa->balance = 0;break;
case 1:RightBalance(myt,pa);
break;
}
}
ptr = pa;
pa = pa->prarent;
}
}
bool InsertItem(AVLTree& myt,KeyType kx)
{
//1.找到位置
AVLNode* pa = myt.head; //head
AVLNode* p = myt.head->prarent;//root
while(p != NULL && p->key != kx)
{
pa = p;
p = kx<p->key ? p->leftchild : p->rightchild;
}
if(p != NULL && p->key == kx) return false;//找到相同的
p = Buynode(pa);
if(pa == myt.head)
{
myt.head->prarent = p;
myt.head->leftchild = p;
myt.head->rightchild = p;
}
else
{
if(p->key < pa->key)
{
pa->leftchild = p;
if(p->key < myt.head->leftchild->key)//维护head结点中最小
{
myt.head->leftchild = p;
}
}
else
{
pa->rightchild = p;
if(p->key > myt.head->rightchild->key)//维护head结点中最大
{
myt.head->rightchild = p;
}
}
AdjustTree(myt,p);//调整函数
}
myt.cursize += 1;
return true;
}