平衡二叉树(Balancedbinary tree)是由阿德尔森-维尔斯和兰迪斯(Adelson-Velskiiand Landis)于1962年首先提出的,所以又称为AVL树。
定义:平衡二叉树或为空树,或为如下性质的二叉排序树:
(1)左右子树深度之差的绝对值不超过1;
(2)左右子树仍然为平衡二叉树.
平衡二叉树可以避免排序二叉树深度上的极度恶化,使树的高度维持在O(logn)来提高检索效率。
平衡二叉树算法思想
若向平衡二叉树中插入一个新结点后破坏了平衡二叉树的平衡性。首先要找出插入新结点后失去平衡的最小子树根结点的指针。然后再调整这个子树中有关结点之间的链接关系,使之成为新的平衡子树。当失去平衡的最小子树被调整为平衡子树后,原有其他所有不平衡子树无需调整,整个二叉排序树就又成为一棵平衡二叉树。失去平衡的最小子树是指以离插入结点最近,且平衡因子绝对值大于1的结点作为根的子树 。
要维持二叉树的平衡需要使用左旋和右旋操作。
右旋(如图1):以A为中心顺时针旋转。实际就是把B移到A的位置,把B的右子树变为A的左子树,在把A作为B的右子树。在平衡二叉树中以平衡因子为2的节点进行右旋。

右旋前 右旋后
右旋代码:
- <span style="font-size:18px;">//右旋
- void RotateRight(tNode** root)
- {
- tNode* p=*root;//图中相当于A
- tNode* rc=p->pleft;//相当于B
- tNode* rrc=rc->pright;//相当于D
- p->pleft=rrc;
- rc->pright=p;
- *root=rc;
- }</span>
左旋(如图):以A为中心逆时针旋转,实际就是:把B移到A的位置,把B的左子树作为A的右子树,再把A变成B的左子树。在平衡树种以平衡因子为-2的节点为中心做左旋操作。

左旋前 左旋后
左旋代码:
- //左旋
- void RotateLeft(tNode** root)
- {
- tNode * p=*root;//相当于A
- tNode* lc=p->pright;//相当于B
- tNode* llc=lc->pleft;//相当于C
- p->pright=llc;
- lc->pleft=p;
- *root=lc;
- }
插入新的节点:
1、LL的情况:
由于在A的左孩子B的左子树上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行一次顺时针旋转操作。 即将A的左孩子B向右上旋转代替A作为根结点,A向右下旋转成为B的右子树的根结点。而原来B的右子树则变成A的左子树。

2、LR情况:
由于在A的左孩子B的右子数上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行两次旋转操作(先逆时针,后顺时针)。即先将A结点的左孩子B的右子树的根结点D向左上旋转提升到B结点的位置,然后再把该D结点向右上旋转提升到A结点的位置。即先使之成为LL型,再按LL型处理。
左平衡代码:
- <span style="font-size:18px;">void AVLTree::LeftBalance(tNode** root)
- {
- tNode* p=*root;
- tNode* c=p->pleft;
- tNode* rc=c->pright;
- switch(c->bf)
- {
- //LL情况
- case 1:
- {
- p->bf=c->bf=0;
- RotateRight(root);
- break;
- }
- //LR情况
- case -1:
- {
- switch(rc->bf)
- {
- case 1:
- {
- c->bf=0;
- p->bf=-1;
- break;
- }
- case 0:
- {
- c->bf=0;
- p->bf=0;
- break;
- }
- case -1:
- {
- c->bf=1;
- p->bf=0;
- break;
- }
- }
- rc->bf=0;
- RotateLeft(&c);
- RotateRight(root);
- break;
- }
- }
- }</span>
左平衡操作在处理LR情况时又根据rc节点也就是图中D的平衡因子分三种情况来确定平衡后图中B和A的平衡因子。其他的平衡因子不变。
怎么来确定A和B左平衡操作后的平衡因子?
假设在LR情况下,新的节点插入后C的平衡因子是1,那么设C的左子树的高度为h则右子树为h-1,B的右子树为h+1,B的平衡因子是-1,则它左子树高度为h,A左子树高度为h+2,它的平衡因子为2,则它的右子树高度为h。在平衡化后,A的左子树以C为根,右子树为D,所以A的平衡因子为0.而B的右子树以A为根,则它的平衡因子为-1.
在C的平衡因子为0,-1的情况下也可以用上面方法推出,平衡后A和B的平衡因子。

3 RR情况:
由于在A的右孩子C 的右子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行一次逆时针旋转操作。即将A的右孩子C向左上旋转代替A作为根结点,A向左下旋转成为C的左子树的根结点。而原来C的左子树则变成A的右子树。
4、RL情况:
由于在A的右孩子C的左子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行两次旋转操作(先顺时针,后逆时针),即先将A结点的右孩子C的左子树。

右平衡代码:
- <span style="font-size:18px;">void AVLTree::RightBalance(tNode** root)
- {
- tNode* p=*root;
- tNode* c=p->pright;
- tNode* lc=c->pleft;
- switch(c->bf)
- {
- //RR情况
- case -1:
- {
- p->bf=-1;
- c->bf=0;
- RotateLeft(root);
- break;
- }
- //RL情况
- case 1:
- {
- switch(lc->bf)
- {
- case 1:
- {
- p->bf=0;
- c->bf=-1;
- break;
- }
- case 0:
- {
- p->bf=0;
- c->bf=0;
- break;
- }
- case -1:
- {
- p->bf=1;
- c->bf=0;
- break;
- }
- lc->bf=0;
- RotateRight(&c);
- RotateLeft(root);
- break;
- }
- }
- }
- }</span>
右平衡在RL的情况下平衡因子的变化也分三种情况。因子推断与上面相同。
左旋,右旋,左平衡,右平衡都齐全了。那么插入新节点操作也只是把他们穿针引线的连起来而已。
插入新的节点代码 :
插入新节点以递归的方式。在插入新节点成功后回溯更新和维护树的平衡性。
- <span style="font-size:18px;">int AVLTree::Insert(tNode** root,int key,int& taller)
- {
- if(*root==NULL)
- {
- tNode* newNode=new tNode(key);
- taller=1;
- *root=newNode;
- return 1;
- }
- else if((*root)->mvalue==key)
- {
- return 0;
- }
- else if(key<(*root)->mvalue)
- {
- if(Insert(&((*root)->pleft),key,taller))
- {
- return 1;
- }
- if(taller)
- {
- switch((*root)->bf)
- {
- case 1:
- LeftBalance(root);
- taller=0;
- break;
- case 0:
- (*root)->bf=1;
- taller=1;
- break;
- case -1:
- (*root)->bf=0;
- taller=0;
- break;
- }
- }
- return 0;
- }
- else if(key>(*root)->mvalue)
- {
- if(Insert(&((*root)->pright),key,taller))
- {
- return 1;
- }
- if(taller)
- {
- switch((*root)->bf)
- {
- case -1:
- RightBalance(root);
- taller=0;
- break;
- case 0:
- (*root)->bf=-1;
- taller=1;
- break;
- case 1:
- (*root)->bf=0;
- taller=0;
- break;
- }
-
- }
- return 0;
- }
- }</span>
删除节点操作:
删除节点操作比插入稍微复杂。插入只需要一次平衡操作即可恢复树的平衡性。而删除节点在删除后,可能需要多次操作才能恢复树的平衡。
要先找到最底部不平衡的子树,将其恢复平衡。在根据情况判断需不需要继续沿着子树向上恢复平衡。
为了找到最底部不平衡的子树,将删除操作迁移到最底部的子树上进行。
删除节点的代码为:
- //返回1表示要删除的点在最底部子树上,删除成功。返回0表示将删除节点向下迁移。
- int AVLTree::deleteNode(tNode* p,tNode* pp,int& key,bool& shorter)
- {
- //p表示当前节点,pp表示当前节点的父节点。key表示要删除元素的键值,
- //shorter 表示是否需要平衡化操作。
- if(p->pleft&&p->pright)
- {
- tNode* s=p->pleft;
- tNode* ps=p;
- while(s->pright!=NULL)
- {
- ps=s;
- s=s->pright;
- }
- p->mvalue=s->mvalue;//覆盖原来要删除的点的值。
- key=s->mvalue;//将关键字的值更改。
- return 0;//没删除成功只是替换了要删除的元素
- }
- tNode* c=NULL;
- if(p->pleft)
- c=p->pleft;
- else
- c=p->pright;
- if(p==pRoot)//pRoot表示树的根
- pRoot=c;
- else
- {
- if(p==pp->pleft)
- {
- pp->pleft=c;
-
- }
- else
- {
- pp->pright=c;
- }
- }
- shorter=true;
- delete p;
- return 1;
- }
删除节点的代码
- int AVLTree::Delete(tNode* root,tNode* parent,int key,bool& shorter)
- {
- if(root==NULL)
- {
- shorter=false;
- return 0;
- }
- else if(root->mvalue==key)
- {
- if(deleteNode(root,parent,key,shorter))
- return 1;
- }
- else if(root->mvalue>=key)
- {
- if(Delete(root->pleft,root,key,shorter))
- {
- if(shorter)
- {
-
- switch(root->bf)
- {
- case 1:
- {
- root->bf=0;
- shorter=true;
- break;
- }
- case 0:
- {
- root->bf=-1;
- shorter=false;
- break;
- }
- case -1:
- {
- <pre name="code" class="cpp"> if(root->pright->bf==0)
- {//为L0情况经过平衡化后子树
- //的高度和删除前一样
- shorter=false;
- }
- else
- //L1,L-1,平衡化后与删除前
- //比子树高度减小1,其父树
- //还需要平衡化操作
- shorter=true;</pre> RightBalance(&root);break;}}}return 1;}return 0;}else if(root->mvalue<key){if(Delete(root->pright,root,key,shorter)){if(shorter){switch(root->bf){case 1:{<br>
- <pre name="code" class="cpp"> if(root->pleft->bf==0)
- shorter=false;
- else
- shorter=true;</pre><br>
- LeftBalance(&root);break;}case 0:{root->bf=1;shorter=false;break;}case -1:{root->bf=0;shorter=true;break;}}}return 1;}return 0;}}
- <pre></pre>
- <p></p>
- <pre></pre>
- 平衡树类定义代码:
- <p></p>
- <p align="left" style=""><span style="font-size:24px; color:#333333"></span></p>
- <pre name="code" class="cpp">struct tNode
- {
- int bf;//平衡因子
- int mvalue;
- tNode* pleft,
- *pright,
- *parent;
- tNode():bf(0),mvalue(0),pleft(0),pright(0),parent(0)
- {
-
- }
- tNode(int ivalue):bf(0),mvalue(ivalue),pleft(0),pright(0),parent(0)
- {
-
- }
- };
- class AVLTree
- {
- public:
- AVLTree(void):pRoot(0){}
- ~AVLTree(void);
- //插入值为key的节点
- int AVL_Insert(int key)
- {
- int taller;
- Insert(&pRoot,key,taller);
- }
- //删除值为key的节点
- int AVL_Delete(int key)
- {
- bool shorter;
- Delete(pRoot,0,key,shorter);
- }
- private:
- //左旋
- void RotateLeft(tNode** root)
- {
- tNode * p=*root;
- tNode* lc=p->pright;
- tNode* llc=lc->pleft;
- p->pright=llc;
- lc->pleft=p;
- *root=lc;
- }
- //右旋
- void RotateRight(tNode** root)
- {
- tNode* p=*root;
- tNode* rc=p->pleft;
- tNode* rrc=rc->pright;
- p->pleft=rrc;
- rc->pright=p;
- *root=rc;
- }
- //左平衡
- void LeftBalance(tNode** root);
- //右平衡
- void RightBalance(tNode** root);
- //插入新的节点
- int Insert(tNode** root,int key,int& taller);
- //删除某个节点
- int Delete(tNode* root,tNode* parent,int key,bool& shorter);
- //删除节点的实际操作
- int deleteNode(tNode* p,tNode* pp,int& key,bool& shorter);
- private:
- tNode* pRoot;
- };
- </pre>
- <pre></pre>
- <p><br>
- </p>
- <p><span style="font-size:18px">程序有Bug,大概的实现方法是这样的。整了几天,还是觉得和方法和代码结合。容易被接受。总结出来也方便以后查看。</span></p>
|