AVL树 = 平衡树 + 搜索树
很多东西想明白了并没有书上说的那么复杂。
比如说AVL树的旋转吧,书上分很多情况,什么单旋、双旋啊,什么RR、LL、LR、RL类型啊,说的那么复杂。
其实,只要明白原则性的东西就好:
左子节点比根小,右子节点比根大。
所以,要是“/”或"\"型的失衡,即三个节点是连续的,必然是把中间的节点转成根,上方的和下方的当子节点;
而要是"<"或">"型失衡,即这种弯曲的,必然是最下方的为根,上方两个节点作为左右子节点。
旋转:在三个节点中选择一个中间大的节点作为根节点,其他的两个节点作为其左右子节点。想办法把/, \, <, >的结构
旋转成 ^的平衡结构!旋转后还得符合BST的大小关系。
最小不平衡点: 离新插入节点最近的,bf为-2或者2的节点。
旋转点:/ , \, <, >结构中的中间大的节点
\ 左旋:以旋转点【中间的节点】为中心,断开旋转点的左子树,将最小不平衡点断开作为旋转点新的左子树,旋转点原来的左子树作为断开的最小不平衡点的新的右子树
/ 右旋:以旋转点【中间的节点】为中心,断开旋转点的右子树,将最小不平衡点断开作为旋转点新的右子树,旋转点原来的右子树作为断开的最小不平衡点的新的左子树
< : 先左旋,变成 /, 再右旋
> : 先右旋,变成 \, 再左旋
其实,旋转或者说重构,它们本质都是减小树高度,即原来的根肯定不能再作为根了,接下来的“微调”只需观察余下节点“大小”关系就可以了。关键其实是削减高度后新根的确定,因为局部的微调用眼睛直接就看出来了。
为什么这样呢?"\"和"/"型好理解,中间的数大小肯定借于上下两个数大小之间;而“<”也">"也是类似的:
比如说
a
/
b
\
c
因为b<a,c>b且c<a,即abc三者的关系是a>c>b,所以c还是中间大的(作为根),那ab就当子节点吧。">"型的同理。
练习实例:
1依据字典序,按照AVL树插入算法依次插入{head,he,tea,teach,twin,hot,toss}。
关键:插入twin时,属于"\"型;插入hot时,属于">"型。
2按AVL插入算法依次插入{55, 31, 11, 37, 46, 73, 63}。
关键:插入11时,属于“/”型;插入46时,属于"<"型;插入73时,属于"\"型;插入63时,属于">"型。
=================
首先还需要明白一个概念->最小不平衡子树的根结点:也就是当你进行插入操作时,找到该需要插入结点的位置并插入后,从该结点起向上寻找(回溯),第一个不平衡的结点即平衡因子bf变为-2或2。
2.1.四种情况,两种分类处理
根据树型结构的不同,我们将分成四种情况来进行旋转处理。
让我们把必须重新平衡的节点叫做a(即上面我们所说的最小不平衡子树的根结点)。由于任意节点最多有两个儿子,因此高度不平衡时,a点的两棵子树的高度差2。容易看出这种不平衡可能出现在下面四种情况中。
1.对a的左儿子的左子树进行一次插入。 (以a的左儿子为中心,右旋一次)
2.对a的左儿子的右子树进行一次插入。 (先以a的左儿子的右子树的根节点k为中心左旋,把k提为最小不平衡点左子树的根;然后再以k为中心右旋,把k提为整颗树的根。先左后右)
3.对a的右儿子的左子树进行一次插入。 (先以a的右儿子的左子树的根节点k为中心右旋,把k提为最小不平衡点右子树的根;然后再以k为中心左旋,把k提为整棵树的根。先右后左)
4.对a的右儿子的右子树进行一次插入。 (以a的右儿子为中心,左旋一次)
进行旋转处理的时候分成两种大类处理:单旋转和双旋转。
旋转前,首先找到最小不平衡节点
然后确定新插入的节点相对于最小不平衡节点的位置,是属于上面所说4种情况中的哪并进行相应的旋转
旋转过程中可能需要把某些节点移动到另一边
下面以图为例作说明
k1是最小不平衡点,新插入的节点是k1的右儿子的右子树,右右插入导致的不平衡,以k2为中心左旋一次,左旋过程中Y被k1子树替换,需要将Y移动为k1的右子树
【k1:最小不平衡点; k2: 中间大的节点,旋转点;以旋转点k2为中心,断开Y,端口最小不平衡点k1,k2之间的联系;将k1作为旋转点的新的左子树,Y作为k1新的右子树】
k3是最小不平衡点,新插入的节点是k3的右儿子的左子树,右左插入导致的不平衡,需向右然后向左旋转两次来保持新的平衡。
第一步先以最小不平衡点k3的右儿子k1的左子树的根节点k2为中心右旋,把k2提为最小不平衡点k3的右子树的根,旋转过程中C被k1替换,需要把c移动到k1的左子树上,如下图
然后新的树变成了一颗以k3为最小不平衡点的右右形式的书,以k2为中心进行左旋,最终达到平衡。
#include <stdio.h>
#include <stdlib.h>
#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 ) // /这种结构,做右旋; k2是不平衡点; k1是旋转点,是新根,是中间大的值;
{
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 ) //从插入点进行递归回溯,找到不平衡点T
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;i<length;i++)
Insert(ElementArry[i],T);
return T;
}
int main(int argc, char const *argv[])
{
// 初始化指针
AvlTree T = ( AvlTree )malloc(sizeof( struct AvlNode ));
T->Left = 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;
}