AVL的引入
BST只保证了中序遍历序列单调非减,但是显然BST的查找,插入,删除算法的时间复杂度均为O(logn),具体取决于树有
多高,因此控制BST的高度对于BST的效率而言至关重要;另一方面维护严格的平衡(叶节点只出现在最后一层)需要很高的管理
成本:才会出现折中的平衡二叉树。为了描述BST的平衡性,引入了平衡因子的概念:
节点的平衡因子=节点左右孩子的深度之差,AVL要求所有节点的平衡因子不超过1。
AVL的算法
AVL的算法与BST的不同之处在于要时时刻刻维护节点的平衡因子。查找、插入、删除算法中,查找是静态过程,可以沿用
BST的Search接口,但是插入和删除则需要在动作完成后维护节点的平衡因子。
两种常用旋转zig和zag
AVL的插入算法
(1)单旋
问题描述:虚线表示在T2或T3处插入一个节点,引起局部子树的高度变高,可能同时有多个失衡节点,最低为g。
解决办法:绕着节点g做一次zag旋转,zag(g)将使子树恢复平衡,并且局部子树的高度也将恢复到插入之前,更高层的子树
也将自动恢复平衡。
(2)双旋
问题描述:虚线表示在T1或T2的下方插入一个新的节点,引起可能多个节点失衡,最低者为g。
解决方法:先做一次zig(p)旋转,再做一次zag(g)旋转,局部子树的高度恢复到插入之前,更高节点的失衡自动恢复。
AVL的删除算法
(1)单旋
问题描述:T0和T1下方的虚线表示至少有一个节点,T2下方表示可能有一个节点,此时删除T3下面的一个节点,将导致节点g
失去平衡。
解决办法:绕着失衡的节点g做一次zig(g)旋转,此时局部子树高度恢复平衡,但是如果删除之前T2下方没有节点的话,局部
子树高度将减1,会引起更高一层的失衡,如此失衡现象向上传播,可能在每一层都要做一次旋转调整。
(2)双旋
问题描述:T1和T2的下方至少有一个节点,此时删除T3下方的一个节点将导致节点g失衡。
解决办法:先做一次zag(p),再做一次zig(g),子树高度减1,更高级祖先仍可能失衡。
统一解决:3+4重构
(1)假设g为最低失衡节点,考察祖孙三代g-p-v,按照中序序列,重命名为a<b<c;
(2)他们总共拥有4棵子树,按照中序序列,将其重命名为T0<T1<T2<T3;
(3)将原先以g为根的子树替换为一棵新子树
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;a->parent=b;
b->rc=c;c->parent=b;
updateHeight(b);
return b;
}
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)
{
return connect34(v,p,g,v->lc,v->rc,p->rc,g->rc);
}
else
{
return coonect34(p,v,g,p->lc,v->lc,v->rc,g->rc);
}
}
else
{
if(IsLCHild(*v)
{
return connect34(g,v,p,g->lc,v->lc,v->rc,p->rc);
}
else
{
return connect34(g,p,v,g->lc,p->lc,v->lc,v->rc);
}
}
}