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种情况中的哪并进行相应的旋转

旋转过程中可能需要把某些节点移动到另一边

 

下面以图为例作说明

avl树旋转java代码 avl树单旋转_子节点

k1是最小不平衡点,新插入的节点是k1的右儿子的右子树,右右插入导致的不平衡,以k2为中心左旋一次,左旋过程中Y被k1子树替换,需要将Y移动为k1的右子树

【k1:最小不平衡点;  k2: 中间大的节点,旋转点;以旋转点k2为中心,断开Y,端口最小不平衡点k1,k2之间的联系;将k1作为旋转点的新的左子树,Y作为k1新的右子树】

 

 

avl树旋转java代码 avl树单旋转_子节点_02

k3是最小不平衡点,新插入的节点是k3的右儿子的左子树,右左插入导致的不平衡,需向右然后向左旋转两次来保持新的平衡。

第一步先以最小不平衡点k3的右儿子k1的左子树的根节点k2为中心右旋,把k2提为最小不平衡点k3的右子树的根,旋转过程中C被k1替换,需要把c移动到k1的左子树上,如下图

avl树旋转java代码 avl树单旋转_Max_03

然后新的树变成了一颗以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;
}