理想平衡与适度平衡
含有n个节点的二叉树高度以[logn]为上界,若树高恰好为[logn]则称为理想平衡树,完全二叉树就是其中之一。
适度平衡
而实际的二叉树关键码趋近与一侧的较多。在渐进意义下的平衡性才有意义。
局部性
在AVL树中,兄弟节点的高度相差不过1。而且还有:
- 经过单次动态修改操作后,至多只有O(1)的局部不再符合限制条件。
总可在O(logn)时间内,使O(1)处的不满足重新满足限制条件。
这点很重要,二叉树的顺序性是在中序遍历下确定的,所以只要在中序遍历不变的情况下可以认为两颗二叉树是等价的。
引入旋转调整的概念:
- zig
- zag
先前在二叉树中提到,孩子高度发生变化,父代很难发现其变化,所以在定义AVL模板类之前,给出关于平衡因子的宏定义。
- BalFac(v) = height(lc(v))-height(rc(v))
- |BalFac(v)|<=1
#define BalFac(x) (stature((x).lc)-stature((x).rc))
#define AvlBalanced(x) ((-2<BalFac(x))&&(BalFac(x)<2))
#define Balanced(x) (stature((x).lc)==stature((x).rc))
给出AVL模板类。
#include "BST.h"
template<typename T>class AVL : public BST<T>{
public:
BinNodePosi(T) insert(const T& e);
bool remove(const T& e);
//其余沿用BST接口
}
而插入算法不同于BST的一点就是插入一个新节点之后,历代祖先可能高度会发生变化,一但高度发生变化,平衡就会打破,所以重平衡是主要解决的问题。
由于前面局部性的定义,考察三个节点,v,p=v->parent,g=p->parent。一旦这个局部调整完成,全树的顺序性必然恢复。(这是为什么?)
宏定义
#define tallerChild(x) (stature((x)->lc)<stature((x)->rc)?x->rc:(//左边高
stature((x)->rc)<stature((x)->lc)?x->lc:(//右边高
IsChild(*(x))?x->lc:x->rc//等高的情况
)
)
)
先给出插入算法。
template<typename T>BinNodePosi(T) AVL<T>::insert(const T& e){
BinNodePosi(T) x = Search(e);//常规的搜索
if(x) return x;//如果x不存在则创建
BinNodePosi(T) xx = new BinNode<T>(e,_hot);
_size++;
for(BinNodePosi(T) g =_hot; g ; g = g->parent){//从_hot开始沿parent指针回溯
if(!AvlBalanced(*g)){//一旦发现不平衡
FromParentTo(*g)=rotateAt(tallerChild(tallerChild(g)));g->p->v
break;//局部调整完毕则退出
}
else//全树没有不平衡的点
updateHeight(g);//逐步更新高度
}
return xx;//不论插入的点存不存在,总能返回。
}
而删除算法则不一样,会出现以下的情况
调整之后,树高没有减少,P的某一祖先会有L-subT高度-1,而出现BalFac = -2的情况
template<typename T>bool AVL<T>::remove(const T& e){
BinNodePosi(T) &x = Search(e);
if(!x) return false;
removeAt(x,_hot);_size--;
//重平衡
for(BinNodePosi(T) g = _hot;g;g=g->parent){
//没有办法提前退出
if(!AvlBalanced(*g)){
FromParentTo(*g) = rotateAt(tallerChild(tallerChild(g)));
}
}
return true;
}
统一重平衡算法
不论是插入或是删除算法,都需要调整一个子树,即v->p->g的结构,而3个节点构成的完全二叉树则完全可以重新构成一棵AVL树。
称之为“3+4重构”
template<typename T>BinNodePosi(T) BST<T>::connect34(BinNodePosi(T) a,BinNodePosi(T) b,BinNodePosi(T) c
,BinNodePosi(T) T0,BinNodePosi(T) T1,BinNodePosi(T) T2,BinNodePosi(T) T3){
a->lc = T0; if(T0) T0->parent = a;
a->rc = T1; if(T1) T1->parent = a;
updateHeight(a);
c->lc = T2; if(T2) T2->parent = c;
c->rc = T3; if(T3) T3->parent = c;
updateHeight(c);
b->lc = a;
b->rc = c;
updateHeight(b);
return b;
}
有了3+4重构则可以完成先前zig以及zag旋转调整的思路。
template<typename T>BinNodePosi(T) BST<T>::rotateAt(BinNodePosi(T) v){
BinNodePosi(T) p = v->parent;
BinNodePosi(T) g = p->parent;
//单旋
if(IsLChild(*p))
if(IsLChild(*v)){
//zig操作
p->parent = g->parent;
return connect34(v,p,g,v->lv,v->rc,g->lc,g->rc);
}
else{
//双旋
//zag-zig操作
v->parent = g->parent;
return connect34(p,v,g,p->lc,p->rc,g->lc,g->rc);
}
else{
if(IsRChild(*v)){
//zag操作
p->parent = g->parent;
return connect34(g,p,v,g->lc,g->rc,v->lc,v->rc);
}
else{
//zig-zag操作
v->parent = g->parent;
return connect34(g,v,p,g->lc,g->rc,p->lc,p->rc);
}
}
}