树--AVL平衡树

1、AVL平衡树

       AVL 平衡树是带有平衡条件的二叉查找树。保证树的深度深度是O(logN),定义一个不平衡的节点的定义是,他的左右子树高度差是2.

       当插入一个节点之后,树可能不平衡,假设这个不平衡点是K1,造成这种不平衡的原因可能有以下四个情况:

        (1)对K1的左儿子的左子树进行插入。

         

        (2)对K1的左儿子的右子树进行插入。

    

        (3)对K1的右儿子的右子树进行插入。


        (4)对K1的右儿子的左子树进行插入。

        上述四种情况(1)(4)是镜像的,(2)(3)也是镜像的。因此理论上是四种情况,实际是两种,但是编程角度而言还是四种情况。平衡树的关键是理解如何保持树的平衡,也就是几个节点的指针之间的转换。也就是AVL平衡树中所说的旋转。自己将四种情况手动画出来,思考其中的指针变化。

        对于(1)(4)情况只需要执行一次单旋转即可实现树的平衡。对与(2)(3)则需要两次旋转。

        下面是代码实现:

        

#ifndef _AVLTree_H
typedef int ElementType;
struct AVLNode;
typedef struct AVLNode *Position;
typedef  struct AVLNode *AVLTree;
AVLTree MakeEmpty(AVLTree T);
Position Find(ElementType x, AVLTree T);
Position FindMax(AVLTree T);
Position FindMin(AVLTree T);
AVLTree Insert(ElementType x, AVLTree T);
AVLTree Delete(ElementType x, AVLTree T);
ElementType Retrieve(Position p);
Position SingleRotateWriteLeft(Position K2);
Position SingleRotateWriteRight(Position K2);
static DoubleRoateRight(Position K3);




struct AVLNode
{
	ElementType Element;
	AVLTree left;
	AVLTree right;
	int Height;
};
#endif
#include"AVLTree.h"

//其实平衡树需要的是高度差信息。这样做可以避免平衡因子的重复计算
//但是丧失了一些简明性。最后的程序很容易比只存储高度复杂。
static int Height(Position P)
{
	if (P == NULL)
		return -1;
	else
		return P->Height;
}

//
//函数前加static将使这个函数只能在本文件内部调用适用于LL
//                 K2            K1
//                /     变成       \ 
//               K1                 K2  
//
static Position SingleRotateWithRight(Position K2)
{
	Position K1;
	K1=K2->left;
	K2->left=K1->right;
	K1->right=K2;
	
	//计算树高
	K1->Height=MAX(Height(K1->left),Height(K1->right))+1;
	K2->Height=MAX(Height(K2->left),Height(K2->right))+1;
	
	return K1;
	

}
//
//函数前加static将使这个函数只能在本文件内部调用适用于RR
//       	K2                K1
//                 \    变成     / 
//                  K1         K2
//
static Position SingleRotateWithRight(Position K2)
{
	Position K1;
	K1=K2->right;
	K2->rihght=K1->left;
	K1->left=K2;
	
	//计算树高
	K1->Height=MAX(Height(K1->left),Height(K1->right))+1;
	K2->Height=MAX(Height(K2->left),Height(K2->right))+1;
	
	return K1;
	
}
//
//双旋转先右再左适用于LR
//          K3                    K3                        K1 
//         /                     /                         /  \
//        K2      右转变成      K1   在左转变成           K2   K3
//          \                  /  
//           K1               K2 
static Position DoubleRotateWithLeft(Position K3)
{   
    
	K3->left = SingleRotateWithRight(K3->left);
	return SingleRotateWithLeft(K3);
}
//双旋转先左后右适用于RL型
//
//          K3                    K3                          K1 
//            \                     \                        /  \
//             K2   左转变成         K1  再右转变成           K3   K2
//            /                        \  
//           K1                         K2 
static Position DoubleRotateWithRight(Position K3)
{
	K3->right = SingleRotateWithLeft(K3->right);
	return SingleRotateWithRight(K3);
}

旋转操作是用在插入和删除操作中。

//插入节点跟二叉树不同的是插入要考虑树的平衡性问题
AVLTree Insert(ElementType X,AVLTree T)
{
	//递归实现插入
	if(T==NULL)
	{
		T=malloc(struct AVLNode);
		if(T==NULL)
			printf("out of space!");
		else 
		{
			T->Element=X;
			T->left=NULL;
			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=SingleRotateWithRight(T);     //LL型  右转
				else 
					T=DoubleRotateWithRight(T);     //LR型需要先右转再左转
			}
		}
		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=SingleRotateWithLeft(T);   //RR 左转
				else
					T=DoubleRotateWithLeft(T);   //RL  先左转再右转
			}
		}
	
		T->Height=MAX(Height(T->left),Height(T->right))+1;
		
		return T;     //返回根节点
	
}


    

猜你喜欢

转载自blog.csdn.net/zl6481033/article/details/81033702