AVL树介绍
AVL树是带有平衡条件的二叉查找树。这个平衡条件必须容易保持,而且它必须保证树的高度是O(logN)。AVL树其每个节点的左子树和右子树高度最多差1(空树的高度为-1),每个节点保留其高度信息。下图中,左边的图是AVL树,而右边的不是。
节点插入
当对一个AVL树插入节点后,只有那些从插入节点到根节点的路径上的节点的平衡可能被改变,因为这些节点的子树可能发生变化。当我们沿着这条路径上行到根并更新平衡信息时,我们可以找到第一个节点,它的新平衡破坏了AVL条件。下面指出如何调整这个节点。
假设必须重新平衡的节点叫做A。由于任意节点最多有两个孩子,因此高度不平衡时,A点的两个子树的高度差2.容易看出,这种不平衡可能出现在下面四种情况中:
1):对A的左儿子的左子树进行了一次插入,可通过一次单旋调整;
2):对A的左儿子的右子树进行了一次插入,可通过一次双旋调整
3):对A的右儿子的左子树进行了一次插入,可通过一次双旋调整
4):对A的右儿子的右子树进行了一次插入,可通过一次单旋调整;
其中,情形1和4是关于A点的镜像对称,情形2和3也是关于A点的镜像对称。单旋较简单,如示例图1所示:首先找到不满足平衡条件的节点(节点3),然后在该节点与该节点的儿子之间(节点4)进行一次单选操作即可。
图1:情形4所进行的单旋转操作
对于双旋,其实用一句话就可以简单概括双旋:假如A点是破坏平衡条件的第一个需要调整的节点,那么双旋发生的是:首先在A的儿子和孙子之间做一次单旋,然后在A和它的新儿子之间再做一次单旋。如示例图2所示,插入节点14之后,节点6不在满足平衡条件。因为是在6的右儿子的左子树进行插入,所以需要进行一次右左双旋。首先在节点6的儿子(节点15)和孙子(节点7)之间进行一次右旋,得到中间结果,然后将节点6和其新儿子(节点7)之间进行左旋,得到最终结果。右左双旋也是同样的道理。
图2 双旋操作
节点删除
回顾一下对于一般的二叉查找树是怎样进行remove操作的。先根据二叉树的性质找到应该删除的结点t(如果有的话):如果t是个叶子结点,直接delete就好;如果它只有一颗非空的子树,那么就让这颗子树继承被删除结点的位子;而如果它有两颗非空的子树,就在右子树X中找到值最小的结点s,将s的data(值)写到t中,再在X中删除s(因为s的左子树一定为空,否则它就不是X中最小的了。另外,当然也可以在左子树中找值最大的结点)
对于AVL树的删除操作,可概括为如下几种情况:
首先在树中搜寻是否有节点的元素值等于需要删除的元素。如未搜索到,直接返回。否则执行以下操作。
(1) 要删除的节点是当前根节点T。
如果左右子树都非空。在高度较大的子树中实施删除操作。分以下两种情况:
A、左子树高度大于右子树高度,将左子树中最大的那个元素赋给当前根节点,然后删除左子树中元素值最大的那个节点。
B、左子树高度小于右子树高度,将右子树中最小的那个元素赋给当前根节点,然后删除右子树中元素值最小的那个节点。
如果左右子树中有一个为空,那么直接用那个非空子树或者是NULL替换当前根节点即可。
(2) 要删除的节点元素值小于当前根节点T值,在左子树中进行删除。
递归调用,在左子树中实施删除。
这个是需要判断当前根节点是否仍然满足平衡条件,如果满足平衡条件,只需要更新当前根节点T的高度信息。 否则,需要进行旋转调整:
如果T的右子节点的左子树的高度大于 T的右子节点的右子树的高度,进行相应的双旋转。否则进行单旋转。
(3) 要删除的节点元素值大于当前根节点T值,在右子树中进行删除。如果T的左子节点的右子树高度大于左子节点的左子树高度,进行双旋,否则进行单旋。过程和(2)类似
(4) 回朔被删除节点到根的路径上的其他节点,检查其平衡因子是否满足要求。
下面通过例子简要说明:
例1:如上图,若删除节点8,则其根节点9仍满足高度平衡,因此不需要旋转,简单调整元素9的平衡因子。然后再删除12,此时符合情况(3),11元素为当前的根T,令它的左子树为S,检查S的左子节点的高度HL与右子节点的高度HR,上图中可以看出HL = -1,HR = 0,所以符合双旋条件(HL < HR)。
例2:如果直接在原图上删除12,那么当前的根仍是11,但此时,11的平衡因子被打破,仍然符合情况(3),此时T的左子树为S,S的左子树高度HL = 0,S的 右子树高度为0,此时不满足双旋条件,故在根12处单旋。
代码实现
头文件:AvlTree.h
typedef int ElementType;
#ifndef _AVLTREE_
#define _AVLTREE_
struct AvlNode;
typedef struct AvlNode *Position;
typedef struct AvlNode *AvlTree;
AvlTree MakeEmpty( AvlTree T );
Position Find( ElementType X, AvlTree T );
Position FindMin( AvlTree T );
Position FindMax( AvlTree T );
AvlTree Insert( ElementType X, AvlTree T );
AvlTree Remove( ElementType X, AvlTree T );
ElementType Retrieve( Position P );
void PrintTree(AvlTree T);
AvlTree Remove(const ElementType X, AvlTree T);
#endif
实现文件:AvlTree.c
#include "AvlTree.h"
#include <stdio.h>
#include <stdlib.h>
struct AvlNode
{
ElementType Element;
AvlTree Left;
AvlTree Right;
int Height;
};
AvlTree MakeEmpty( AvlTree T )
{
if (T != NULL)
{
MakeEmpty(T->Left);
MakeEmpty(T->Right);
free(T);
}
return NULL;
}
Position Find( ElementType X, AvlTree T )
{
if (T == NULL)
return NULL;
if (X < T->Element)
return Find(X, T->Left);
else if (X > T->Element)
return Find(X, T->Right);
else
return T;
}
Position FindMin( AvlTree T )
{
if (T == NULL)
return NULL;
/*if (T->Left == NULL)
return T;
else
return FindMin(T->Left);*/
while (T->Left != NULL)
T = T->Left;
return T;
}
Position FindMax( AvlTree T )
{
if (T == NULL)
return NULL;
/*if (T->Right == NULL)
return T;
else
return FindMax(T->Right);*/
while (T->Right)
T = T->Right;
return T;
}
static int Height( Position P )
{
if (P == NULL)
return -1;
else
return P->Height;
}
static int Max( int Lhs, int Rhs )
{
return Lhs > Rhs ? Lhs : Rhs;
}
static Position SingleRotateWithLeft( Position K2)
{
Position K1;
K1 = K2->Left;
K2->Left = K1->Right;
K1->Right = K2;
K2->Height = Max(Height(K2->Left), Height(K2->Right)) + 1;
K1->Height = Max(Height(K1->Left), Height(K1->Right)) + 1;
return K1;
}
static Position SingleRotateWithRight( Position K1)
{
Position K2 = K1->Right;
K1->Right = K2->Left;
K2->Left = K1;
K1->Height = Max(Height(K1->Left), Height(K1->Right)) + 1;
K2->Height = Max(Height(K2->Left), Height(K2->Right)) + 1;
return K2;
}
/* Do the left-right double rotation */
static Position DoubleRotateWithLeft( Position K3 )
{
K3->Left = SingleRotateWithRight(K3->Left);
return SingleRotateWithLeft(K3);
}
/* Do the right-left double rotation */
static Position DoubleRotateWithRight( Position K1 )
{
K1->Right = SingleRotateWithLeft(K1->Right);
return SingleRotateWithRight(K1);
}
AvlTree Insert( ElementType X, AvlTree T )
{
if (T == NULL)
{
T = (AvlTree)malloc(sizeof(struct AvlNode));
if (T == NULL)
{
printf("Out of space!!\n");
return T;
}
else
{
T->Left = T->Right = NULL;
T->Element = X;
}
}
else if (X < T->Element)
{
T->Left = Insert(X, T->Left);
if (Height(T->Left) - Height(T->Right) == 2)
{
//插入之后,引起不平衡,若是在左儿子的左子树中插入,即左-左,只需左单旋即可;
if (X < T->Left->Element)
T = SingleRotateWithLeft(T);
else
//若是在左儿子的右子树中插入,即左-右,需要左双旋
T = DoubleRotateWithLeft(T);
}
}
else if (X > T->Element)
{
T->Right = Insert(X, T->Right);
if (Height(T->Right) - Height(T->Left) == 2)
{
if (X > T->Right->Element)
T = SingleRotateWithRight(T);
else
T = DoubleRotateWithRight(T);
}
}
T->Height = Max(Height(T->Left), Height(T->Right)) + 1;
return T;
}
ElementType Retrieve( Position P )
{
return P->Element;
}
void PrintTree(AvlTree T)
{
if (T != NULL)
{
PrintTree(T->Left);
printf("%3d", Retrieve(T));
PrintTree(T->Right);
}
}
AvlTree Remove(ElementType X, AvlTree T)
{
if(T == NULL)
return NULL;//没有找到要删除的值,do nothing
if(X < T->Element)
{
T->Left = Remove(X, T->Left);
if(Height(T->Right)-Height(T->Left)==2)
{
//右子树比左子树高2,那么在删除操作之前右子树比左子树高1,
//也就是说T的右子树必然不为空,甚至必然有非空子树(高度至少为1).
AvlTree s = T->Right;
if(Height(s->Left)>Height(s->Right))
T = DoubleRotateWithRight(T);//右双旋转
else
T = SingleRotateWithRight(T);//右单旋转
}
else
//不需要调整就满足平衡条件的话,只需要重新计算其高度就好
T->Height=Max(Height(T->Left),Height(T->Right))+1;
}
else if(X>T->Element)
{
T->Right = Remove(X,T->Right);
if(Height(T->Left)-Height(T->Right)==2)
{
AvlTree s=T->Left;
if(Height(s->Right)>Height(s->Left))
T = DoubleRotateWithLeft(T);//左双旋转
else
T = SingleRotateWithLeft(T);//左单旋转
}
else
T->Height=Max(Height(T->Left),Height(T->Right))+1;
}
else
{
if(T->Left&&T->Right) //T的左右子树都非空,把Remove操作转移到只有一个非空子树的结点或者叶子结点上去
{
if( Height(T->Left) > Height(T->Right) )
//把Remove操作往更高的那颗子树上转移
{
//左子树中的最大值
T->Element=FindMax(T->Left)->Element;
T->Left = Remove(T->Element,T->Left);
}
else
{
//右子树中的最小值
T->Element=FindMin(T->Right)->Element;
T->Right = Remove(T->Element,T->Right);
}
}
else
{
AvlTree oldnode = T;
T=T->Left?T->Left:T->Right;
free(oldnode);
}
}
return T;
}