1.AvlTree的定义
AVL (Adelson Velskii和 Landis)树是带有平衡条件的二叉查找树。这个平衡条件必须容易保持,而且它必须保证树的深度是O(log N)。最简单的想法是要求左右子树具有相同的高度。
一般限制为:一棵AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树。(空树的高度定义为-1,树中叶子的高度为0,往根上递增)
如上图中,左边就是一棵AVL树,而右边则不是一棵AVL树,因为节点7的左子树高度为2,右子树的高度为0。
2.插入操作的问题
在对AVL树进行插入操作的时候,隐含的困难在于,插入一个节点可能破坏AVL树的平衡特性。例如在上图中插入一个节点6,那么如果不进行后续处理就会破坏树的平衡性。因为8的左子树深度为1,而右子树深度为-1.
所以一般发生这种情况,我们需要把AVL树的平衡性质恢复之后才能算是插入这一步骤完成。事实上,我们只需要根据树的实际结构进行几种简单的旋转(rotation)操作就可以让树恢复AVL树的平衡性质。
2.1.四种情况,两种分类处理
根据树型结构的不同,我们将分成四种情况来进行旋转处理。
让我们把必须重新平衡的节点叫做a。由于任意节点最多有两个儿子,因此高度不平衡时,a点的两棵子树的高度差2。容易看出这种不平衡可能出现在下面四种情况中。
1.对a的左儿子的左子树进行一次插入。
2.对a的左儿子的右子树进行一次插入。
3.对a的右儿子的左子树进行一次插入。
4.对a的右儿子的右子树进行一次插入。
进行旋转处理的时候分成两种大类处理:单旋转和双旋转。
我们知道之所以会出现不平衡是由于进行插入操作之后某些节点的深度过深导致不平衡。上图中是由于上一步在子树Z的底部进行插入操作之后导致k1节点的左右子树失衡。按照AVL树的定义,此时k1右子树的深度比k1左子树的深度大2。按照上图的方式进行旋转之后k2的左子树深度加1,而右子树深度不变,因此重新回到平衡。
观察上图我们可以发现,在整个旋转的过程中变化的其实只有k1,k2两个节点,三颗子树没有任何变化。
另一种情况的单旋转:
当对(2)a的左儿子的右子树进行一次插入以及(3)对a的右儿子的左子树进行一次插入的时候由于较深的子树处于结构的中间,因此仅用单旋转并不能解决问题,这个时候,我们必须分别在两个节点之间使用两次单旋转,即一次双旋转使AVL树重新恢复平衡。
我们观察到,二叉树的右边可以对k1-k2两个节点先进行一次SingleRotateWithLeft变换
然后把k1及其子树看作是单旋转中的第一种情况中的Z,然后这个时候对k3和k2之间进行一次SingleRotateWithRight变换即可达到平衡的目的。
上面的思路其实很简单,将深度过深的树从整棵树的中间往边上转移,然后就可以参考单旋转中的操作来解决不平衡的问题了。
下面我们可以看到另一种情况的双旋转
2.2.旋转操作中的本质
在上面的四种旋转操作中我们可以看到,其实整个操作中发生变化的点很少,单旋转中只有(k1 k2)两个点,双旋转中只有(k1 k2)、(k3 k2)三个点,其余的子树只有父节点发生了变化,但是这与子树本身没有任何关系。
在具体的代码实现中我们应该注意,因为变化的只有两个或是三个节点,因此旋转操作之后更新高度只需要对这两个或是三个节点更新。(双旋转操作中是分两组更新的!)
3.具体实现代码
#include
#include
#define Max( a , b ) ( (a) > (b) ? (a) : (b) )
typedef int ElementType;
struct AvlNode;
typedef struct AvlNode *Position;
typedef struct AvlNode *AvlTree;
struct AvlNode
{
ElementType Element;
AvlTree Left;
AvlTree Right;
int Height;
};
static int Height( Position P )
{
if ( P == NULL )
return -1;
else
return P->Height;
}
/*This function can be called only if k2 has a left child*/
/*Perform a rotate between a node (k2) and its left child*/
/*Update heights , then return new root*/
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) , k2->Height ) + 1;
return k1; //new root
}
static Position SingleRotateWithRight( Position k2 )
{
Position k1;
k1 = k2->Right;
k2->Right = k1->Left;
k1->Left = k2;
k2->Height = Max( Height( k2->Left ) , Height( k2->Right ) ) + 1;
k1->Height = Max( Height( k1->Right ) , k2->Height ) + 1;
return k1;
}
/*This function can be called only if k3 has a left*/
/*child and k3's left child has a right child*/
/*Do the left-right double rotation*/
/*Update heights,then return new root*/
static Position DoubleRotateWithLeft( Position k3 )
{
/*Rotate between k1 and k2*/
k3->Left = SingleRotateWithRight( k3->Left );
/*Rotate between k3 and k2*/
return SingleRotateWithLeft( k3 );
}
static Position DoubleRotateWithRight( Position k3 )
{
/*Rotate between k1 and k2*/
k3->Right = SingleRotateWithLeft( k3->Right );
/*Rotate between k3 and k2*/
return SingleRotateWithRight( k3 );
}
AvlTree Insert( ElementType X , AvlTree T )
{
if ( T == NULL )
{
/*Create and return a one-node tree */
T = ( AvlTree )malloc (sizeof( struct AvlNode ));
if ( T == NULL )
printf("Out of space!!!\n");
else
{
T->Element = X ; T->Height = 0 ; //The height of the leef is 0 !!! Different from the normal tree!
T->Left = T->Right = NULL;
}
}
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 );
}
/* Else X is in the tree already; we'll do nothing!*/
T->Height = Max( Height( T->Left) , Height( T->Right ) ) + 1;
return T;
}
AvlTree Init_AvlTree(AvlTree T, ElementType *ElementArry,int length)
{
int i;
//逐个插入查找二叉树中
T->Element = ElementArry[0]; //在初始化的过程中直接把第一个点作为根节点。
for(i=1;iLeft = NULL;
T->Right = NULL;
Position Temp;
ElementType ElementArry[11]= {15,6,18,3,7,17,20,2,4,13,9};
/*Initialize the AvlTree*/
T = Init_AvlTree( T , ElementArry , 11 );
printf("%d\n", T->Left->Right->Left->Element );
return 0;
}