近日觉得没事干,就随便拿来本数据结构,从中发现AVL树十分有趣,便决定把它实现了。
下面是原理:
AVL树又叫高度平衡树,它满足两个条件:
(1)|h(Tl)-h(Tr)|<=1,其中h(T)表示树T的高度;
(2)Tl和Tr都是高度平衡树
因为高度平衡树的特性,所以它能够保证查找的最坏复杂度O(log2n).
查找的实现十分简单如下:
template<class D,class K>
BOOL mAVLTree<D,K>::search(const K &k, D &d) const
{
mAVLTreeNode<D,K> * current=m_pAVLTree.m_pHeadAVLTree;
while(current)
{
if(k<current->pKey)
{
current=current->pLlink;
}
else if(k>current->pKey)
{
current=current->pRlink;
}
else
{
return TRUE;
}
}
return FALSE;
}
插入和删除的实现较为复杂。而这两个操作中最复杂的就是删除。那么,先谈谈如何插入。其时间复杂度为O(log2n).当你对AVL树插入一个结点时,如果是一棵空树,那么就直接插入根结点。如果插入的是非空树,那么只有4中情况需要进行位置调整。
第一种是RR型如下图显示:
图上A是表示最后一个产生变化的结点(即还没有插入结点时为+1并且在插入操作中的搜索路径上的结点)或者A就是根结点(从根结点开始的经过的路径上都是完全平衡balance=0的结点(不包括根结点))。B表示在A之后的搜索路径上,在插入前的完全平衡balance=0的结点。c表示插入结点的位置。该图的符号表示插入结点后所产生的变化。就因为产生了这样的变化所以就需要调整位置,调整位置的方法如下图所示:
图中*号表示平衡系数为0(balance=0)。
另外第二种LL型跟以上的RR型类似。
那么下面介绍第三种类型RL型。情况如下图所示:
A,B与之前的表示一样,X与B一样。对于这种型态,根据插入结点是X的左还是右,会得到不同的变化之后的balance值。上图插入的是X的右子树.
这种情况的位置调整如下图所示:
同样的,第四种情况LR型与上面的RL型似。
具体的实现代码如下:
//插入AVL树
template<class D,class K>
HeadNode<D,K> & mAVLTree<D,K>::Insert(const K &k, const D &d)
{
mAVLTreeNode<D,K> *t,*s,*p,*father;//t是s的父亲,s是最后一个不平衡结点或者是树的HEAD结点,p是插入结点的位置,father为p的父亲
HeadNode<D,K> * head;
if(!m_pAVLTree.m_pHeadAVLTree)
{
p=new mAVLTreeNode<D,K>();
p->pData=d;
p->pKey=k;
m_pAVLTree.m_pHeadAVLTree=p;
return m_pAVLTree;
}
head=&m_pAVLTree;
t=m_pAVLTree.m_pHeadAVLTree;
s=m_pAVLTree.m_pHeadAVLTree;
p=m_pAVLTree.m_pHeadAVLTree;
father=p;
//查询插入位置
while(p)
{
if(k<p->pKey)
{
p=father->pLlink;
if(p==NULL)
{
p=new mAVLTreeNode<D,K>();
father->pLlink=p;
break;
}
else
{
if(p->pBalance!=0)
{
t=father;s=p;
}
father=p;
}
}
else if(k>p->pKey)
{
p=father->pRlink;
if(p==NULL)
{
p=new mAVLTreeNode<D,K>();
father->pRlink=p;
break;
}
else
{
if(p->pBalance!=0)
{
t=father;s=p;
}
father=p;
}
}
else
{
printf("find the same key word!!!");
return m_pAVLTree;
}
}
//插入
p->pKey=k;
p->pData=d;
p->pBalance=0;
p->pLlink=NULL;
p->pRlink=NULL;
//调整结点
mAVLTreeNode<D,K> * q,* r;
int a=0;
if(k<s->pKey)
{
r=s->pLlink;
q=r;
}
else
{
r=s->pRlink;
q=r;
}
while(q!=p)
{
if(k<q->pKey)
{
q->pBalance=-1;
q=q->pLlink;
}
if(k>q->pKey)
{
q->pBalance=1;
q=q->pRlink;
}
}
if(k<s->pKey)
a=-1;
else
a=1;
if(s->pBalance==0)
{
s->pBalance=a;
head->m_pTreeHeight+=1;
return m_pAVLTree;
}
if(s->pBalance==-a)
{
s->pBalance=0;
return m_pAVLTree;
}
//单向转动
if(r->pBalance==a)
{
q=r;
if(a==-1) //LL型
{
s->pLlink=r->pRlink;
r->pRlink=s;
s->pBalance=0;
r->pBalance=0;
}
else if(a==1)//RR型
{
s->pRlink=r->pLlink;
r->pLlink=s;
s->pBalance=0;
r->pBalance=0;
}
}
else //双重转动
{
if(a==-1)// LR型
{
q=r->pRlink;
r->pRlink=q->pLlink;
q->pLlink=r;
s->pLlink=q->pRlink;
q->pRlink=s;
if(q->pBalance==a)
{
s->pBalance=-a;
r->pBalance=0;
}
else if(q->pBalance==0) //当是新插入的结点时
{
s->pBalance=0;
r->pBalance=0;
}
else if(q->pBalance==-a)
{
s->pBalance=0;
r->pBalance=a;
}
}
else if(a==1) //RL型
{
q=r->pLlink;
r->pLlink=q->pRlink;
q->pRlink=r;
s->pRlink=q->pLlink;
q->pLlink=s;
if(q->pBalance==a)
{
s->pBalance=-a;
r->pBalance=0;
}
else if(q->pBalance==0) //当是新插入的结点时
{
s->pBalance=0;
r->pBalance=0;
}
else if(q->pBalance==-a)
{
s->pBalance=0;
r->pBalance=a;
}
}
q->pBalance=0;
}
if(head->m_pHeadAVLTree==s)
{
head->m_pHeadAVLTree=q;
}
else
{
if(s==t->pRlink)
t->pRlink=q;
else
t->pLlink=q;
}
return m_pAVLTree;
}
最后,我们来解决最复杂的删除结点操作。删除的时间复杂度与树的高度有关为O(h).删除可以分为两种情况:1、是删除叶结点。在这种情况下,前提条件需要建立一个堆栈S,用来保存current经过的结点。current总是son的父亲结点,B()=balance。有4条规则:
(1)如果B(current)=0,则因子树高度的降低不影响以current为根子树型的高度,故如果son=LLINK(current),则B(current)<- +1,否则B(current)<- -1,并终止整个过程。
(2)如果B(current)= +1且son=RLINK(current),或者B(current)=-1且son=LLINK(current),则B(current)=0,且current的高度减1。此时令son<-current,current<=S,然后继续。
(3)如果B(current)= +1 且son=LLINK(current),则此时current的平衡系数等于+2,根据下图的情况进行相应的变换。
第一种情况删除左边,+右平衡型
做单一转动后结果如下图:
由于树型高度不变,从而删除算法到此结束.
第二种情况删除左边,+右+型如下图所示:
单一转动变换后如下图所示:
如果不是根,又因为变换导致高度减少1所以平衡过程还要继续。
第三种情况 删除左边 + 右- 左*型,如下图所示:
双向旋转变换后如下图所示:
变换之后,如果Current不是根结点,则平衡过程还要继续,因为变换之后子书的高度减1。其实,这里根据B的情况的不同应该分成3种情况,上图只介绍了其中一种情况。其他情况只要根据B(B)的值去改变其他的B()值就可以了。如果B(B)=0,则变换后B(B)=0,B(A)=B(C)=0;如果B(B)=+1,则变换后的B(B)=0,B(A)=-1,B(C)=0;如果B(B)=-1,则变换后的B(B)=0,B(A)=0,B(C)=+1;
(4)如果B(current)=-1且son=RLINK(current),则此时所发生的情况与(3)的正好相反,操作也类似。
2.删除的第二种情况是删除非叶结点。在这种情况下,如果有右子树那么只需要找该结点的后续结点。没右子树只有左子树时,只需和左子结点交换位置就可以了,因为平衡树的关系,左子树只有一个结点。在交换位置的同时,将该结点放入堆栈S,son指向该结点的左子树,current指向该结点。然后就跟着再进行删除叶结点的操作。
具体代码如下:
//删除叶结点的操作
template<class D,class K>
void mAVLTree<D,K>::UpdateTreeStructure(mAVLTreeNode<D,K> *current, mAVLTreeNode<D,K> *son,
mAVLTreeNode<D,K> *pTDelP,mAVLTreeNode<D,K> * parent,
mStack<mAVLTreeNode<D,K> > &pStack)
{
current=pStack.Pop();
//以下是删除叶结点并进行相应既平衡变换
while(current)
{
if(current->pBalance==0)
{
if(son==current->pLlink)
{
current->pBalance=1;
}
else
{
current->pBalance=-1;
}
if(parent->pLlink==pTDelP)
{
parent->pLlink=NULL;
}
else
{
parent->pRlink=NULL;
}
delete pTDelP;
return;
}
if((current->pBalance==1 && son==current->pRlink) || (current->pBalance==-1&& son==current->pLlink))
{
current->pBalance=0;
son=current;
current=pStack.Pop();
}
else if(current->pBalance==1 && son==current->pLlink)
{
if(current->pRlink->pBalance==0)// + 右平衡型
{
mAVLTreeNode<D,K> * r=current->pRlink,*pp=NULL;
current->pRlink=r->pLlink;
r->pLlink=current;
r->pBalance=-1;
pp=pStack.Pop();
if(pp!=NULL) //不是根结点
{
if(current==pp->pLlink)
{
pp->pLlink=r;
}
else
{
pp->pRlink=r;
}
}
else
{
m_pAVLTree.m_pHeadAVLTree=r;
}
if(parent->pLlink==pTDelP)
{
parent->pLlink=NULL;
}
else
{
parent->pRlink=NULL;
}
delete pTDelP;
return;
}
else if(current->pRlink->pBalance==1)// + 右+型
{
mAVLTreeNode<D,K> * r=current->pRlink,*pp=NULL;
current->pRlink=r->pLlink;
r->pLlink=current;
r->pBalance=0;
current->pBalance=0;
pp=pStack.Pop();
if(pp!=NULL) //不是根结点
{
if(current==pp->pLlink)
{
pp->pLlink=r;
}
else
{
pp->pRlink=r;
}
}
else
{
m_pAVLTree.m_pHeadAVLTree=r;
}
son=r;
current=pp;
}
else if(current->pRlink->pBalance==-1) // + 右- 左*型
{
if(current->pRlink->pLlink->pBalance==0)
{
mAVLTreeNode<D,K> * rl=current->pRlink->pLlink;
mAVLTreeNode<D,K> * r=current->pRlink,*pp=NULL;
r->pLlink=rl->pRlink;
rl->pRlink=r;
current->pRlink=rl->pLlink;
rl->pLlink=current;
r->pBalance=0;
rl->pBalance=0;
current->pBalance=0;
pp=pStack.Pop();
if(pp!=NULL) //不是根结点
{
if(current==pp->pLlink)
{
pp->pLlink=rl;
}
else
{
pp->pRlink=rl;
}
}
else
{
m_pAVLTree.m_pHeadAVLTree=rl;
}
son=rl;
current=pp;
}
else if(current->pRlink->pLlink->pBalance==1)
{
mAVLTreeNode<D,K> * rl=current->pRlink->pLlink;
mAVLTreeNode<D,K> * r=current->pRlink,*pp=NULL;
r->pLlink=rl->pRlink;
rl->pRlink=r;
current->pRlink=rl->pLlink;
rl->pLlink=current;
r->pBalance=0;
rl->pBalance=0;
current->pBalance=-1;
pp=pStack.Pop();
if(pp!=NULL) //不是根结点
{
if(current==pp->pLlink)
{
pp->pLlink=rl;
}
else
{
pp->pRlink=rl;
}
}
else
{
m_pAVLTree.m_pHeadAVLTree=rl;
}
son=rl;
current=pp;
}
else if(current->pRlink->pLlink->pBalance==-1)
{
mAVLTreeNode<D,K> * rl=current->pRlink->pLlink;
mAVLTreeNode<D,K> * r=current->pRlink,*pp=NULL;
r->pLlink=rl->pRlink;
rl->pRlink=r;
current->pRlink=rl->pLlink;
rl->pLlink=current;
r->pBalance=1;
rl->pBalance=0;
current->pBalance=0;
pp=pStack.Pop();
if(pp!=NULL) //不是根结点
{
if(current==pp->pLlink)
{
pp->pLlink=rl;
}
else
{
pp->pRlink=rl;
}
}
else
{
m_pAVLTree.m_pHeadAVLTree=rl;
}
son=rl;
current=pp;
}
}
}
else if(current->pBalance==-1 && son==current->pRlink)
{
if(current->pLlink->pBalance==0)// - 左平衡型
{
mAVLTreeNode<D,K> * l=current->pLlink,*pp=NULL;
current->pLlink=l->pRlink;
l->pRlink=current;
l->pBalance=1;
pp=pStack.Pop();
if(pp!=NULL) //不是根结点
{
if(current==pp->pLlink)
{
pp->pLlink=l;
}
else
{
pp->pRlink=l;
}
}
else
{
m_pAVLTree.m_pHeadAVLTree=l;
}
if(parent->pLlink==pTDelP)
{
parent->pLlink=NULL;
}
else
{
parent->pRlink=NULL;
}
delete pTDelP;
return;
}
else if(current->pLlink->pBalance==1)// - 左-型
{
mAVLTreeNode<D,K> * l=current->pLlink,*pp=NULL;
current->pLlink=l->pRlink;
l->pRlink=current;
l->pBalance=0;
current->pBalance=0;
pp=pStack.Pop();
if(pp!=NULL) //不是根结点
{
if(current==pp->pLlink)
{
pp->pLlink=l;
}
else
{
pp->pRlink=l;
}
}
else
{
m_pAVLTree.m_pHeadAVLTree=l;
}
son=l;
current=pp;
}
else if(current->pLlink->pBalance==-1) // - 左+ 右*型
{
if(current->pLlink->pRlink->pBalance==0)
{
mAVLTreeNode<D,K> * lr=current->pLlink->pRlink;
mAVLTreeNode<D,K> * l=current->pLlink,*pp=NULL;
l->pRlink=lr->pLlink;
lr->pLlink=l;
current->pLlink=lr->pRlink;
lr->pRlink=current;
l->pBalance=0;
lr->pBalance=0;
current->pBalance=0;
pp=pStack.Pop();
if(pp!=NULL) //不是根结点
{
if(current==pp->pLlink)
{
pp->pLlink=lr;
}
else
{
pp->pRlink=lr;
}
}
else
{
m_pAVLTree.m_pHeadAVLTree=lr;
}
son=lr;
current=pp;
}
else if(current->pLlink->pRlink->pBalance==1)
{
mAVLTreeNode<D,K> * lr=current->pLlink->pRlink;
mAVLTreeNode<D,K> * l=current->pLlink,*pp=NULL;
l->pRlink=lr->pLlink;
lr->pLlink=l;
current->pLlink=lr->pRlink;
lr->pRlink=current;
l->pBalance=-1;
lr->pBalance=0;
current->pBalance=0;
pp=pStack.Pop();
if(pp!=NULL) //不是根结点
{
if(current==pp->pLlink)
{
pp->pLlink=lr;
}
else
{
pp->pRlink=lr;
}
}
else
{
m_pAVLTree.m_pHeadAVLTree=lr;
}
son=lr;
current=pp;
}
else if(current->pLlink->pRlink->pBalance==-1)
{
mAVLTreeNode<D,K> * lr=current->pLlink->pRlink;
mAVLTreeNode<D,K> * l=current->pLlink,*pp=NULL;
l->pRlink=lr->pLlink;
lr->pLlink=l;
current->pLlink=lr->pRlink;
lr->pRlink=current;
l->pBalance=0;
lr->pBalance=0;
current->pBalance=1;
pp=pStack.Pop();
if(pp!=NULL) //不是根结点
{
if(current==pp->pLlink)
{
pp->pLlink=lr;
}
else
{
pp->pRlink=lr;
}
}
else
{
m_pAVLTree.m_pHeadAVLTree=lr;
}
son=lr;
current=pp;
}
}
}
}
if(parent->pLlink==pTDelP)
{
parent->pLlink=NULL;
}
else
{
parent->pRlink=NULL;
}
delete pTDelP;
}
//删除结点的操作方法
template<class D,class K>
BOOL mAVLTree<D,K>::DeleteNode(const K &k)
{
mAVLTreeNode<D,K> * current=NULL;
mAVLTreeNode<D,K> * son=m_pAVLTree.m_pHeadAVLTree;
mAVLTreeNode<D,K> * pTDelP=NULL; //真实删除点的位置
mAVLTreeNode<D,K> * parent=NULL; //真实删除点的位置的父亲结点
mStack<mAVLTreeNode<D,K>> pStack;
if(son==NULL)
{
return FALSE;
}
while(son)
{
if(k<son->pKey)
{
mStackNode<mAVLTreeNode<D,K>> * pStackNode=new mStackNode<mAVLTreeNode<D,K>>();
pStackNode->pNode=son;
pStack.Push(pStackNode);
current=son;
son=son->pLlink;
}
else if(k>son->pKey)
{
mStackNode<mAVLTreeNode<D,K>> * pStackNode=new mStackNode<mAVLTreeNode<D,K>>();
pStackNode->pNode=son;
pStack.Push(pStackNode);
current=son;
son=son->pRlink;
}
else
{
break;
}
}
if(son==NULL)
return FALSE;
parent=current;
pTDelP=son;
if(son->pRlink) //如果删除的结点有右子树
{
while(son->pRlink)
{
mAVLTreeNode<D,K> * p=son->pRlink;
while(p->pLlink)
{
p=p->pLlink;
}
while(son)//下移删除结点
{
if(p->pKey<son->pKey)
{
mStackNode<mAVLTreeNode<D,K>> * pStackNode=new mStackNode<mAVLTreeNode<D,K>>();
pStackNode->pNode=son;
pStack.Push(pStackNode);
current=son;
son=son->pLlink;
}
else if(p->pKey>son->pKey)
{
mStackNode<mAVLTreeNode<D,K>> * pStackNode=new mStackNode<mAVLTreeNode<D,K>>();
pStackNode->pNode=son;
pStack.Push(pStackNode);
current=son;
son=son->pRlink;
}
else
{
break;
}
}
//交换删除结点的值
D dd;
K kk;
dd=son->pData;
kk=son->pKey;
son->pData=pTDelP->pData;
son->pKey=pTDelP->pKey;
pTDelP->pData=dd;
pTDelP->pKey=kk;
pTDelP=son;
parent=current;
}
UpdateTreeStructure(current,son,pTDelP,parent,pStack);
}
else if(son->pLlink) //只有一个左结点的情况
{
mStackNode<mAVLTreeNode<D,K>> * pStackNode=new mStackNode<mAVLTreeNode<D,K>>();
pStackNode->pNode=son;
pStack.Push(pStackNode);
current=son;
son=son->pLlink;
D dd;
K kk;
dd=son->pData;
kk=son->pKey;
son->pData=pTDelP->pData;
son->pKey=pTDelP->pKey;
pTDelP->pData=dd;
pTDelP->pKey=kk;
pTDelP=son;
parent=current;
UpdateTreeStructure(current,son,pTDelP,parent,pStack);
}
else //删除叶结点
{
UpdateTreeStructure(current,son,pTDelP,parent,pStack);
}
return TRUE;
}
最后给出主要AVL树类:
//AVL树
template<class D,class K>
class mAVLTree
{
protected:
HeadNode<D,K> m_pAVLTree;
public:
mAVLTree()
{
m_pAVLTree.m_pTreeHeight=0;
m_pAVLTree.m_pHeadAVLTree=NULL;
}
mAVLTree(HeadNode<D,K> &pAVLTree)
{
m_pAVLTree.m_pTreeHeight=pAVLTree.m_pTreeHeight;
m_pAVLTree.m_pHeadAVLTree=pAVLTree.m_pHeadAVLTree;
}
~mAVLTree(){}
BOOL search(const K & k,D & d)const;
HeadNode<D,K> & Insert(const K & k,const D & d);
int Depth(mAVLTreeNode<D,K> * pTree);
void print(mAVLTreeNode<D,K> * pTree);
//删除结点,平衡树的变换过程
void UpdateTreeStructure(mAVLTreeNode<D,K> * current,mAVLTreeNode<D,K> * son,
mAVLTreeNode<D,K> * pTDelP,mAVLTreeNode<D,K> *parent,mStack<mAVLTreeNode<D,K>> &pStack);
BOOL DeleteNode(const K & k);
//检测平衡树
BOOL IsBalanceTree(mAVLTreeNode<D,K> * pTree);
//获取树
mAVLTreeNode<D,K>* GetTree(){return m_pAVLTree.m_pHeadAVLTree;}
};