AVL树

本文深入解析AVL树,一种自平衡的二叉查找树,阐述其特点、查找、插入、删除操作及旋转调整过程,并提供具体实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个儿子子树的高度最大差别为一,所以它也被称为高度平衡树

查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。

       对二叉树的平衡调整过程,主要包含四种旋转操作:LL,LR,RR,RL 。

 LR由当前节点左儿子的一次RR旋转和当前节点的一次LL旋转构成。同理, RR由当前节点右儿子的一次LL旋转和当前节点的一次RR旋转构成。

如图:

AVL树的 插入 和删除均要调整重新构成平衡树。

具体实现代码即注释如下:Head.h    fun.c    AvlTree.c

Head.h   

#ifndef HEAD_H_
#define HEAD_H_

#include <stdio.h>
#include <stdlib.h>

typedef int ElementType;
typedef struct AvlNode              // AVL树的节点
{
	ElementType data;
	struct AvlNode *left;           // 左孩子
	struct AvlNode *right;          // 右孩子
	int Height;
}*Position,*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  Delete(ElementType x,AvlTree T);
ElementType Retrieve(Position P);
void Display(AvlTree T);

#endif /* HEAD_H_ */


 fun.c   

#include"Head.h"
/*
 *   初始化AVL树
 */
AvlTree MakeEmpty(AvlTree T)
{
	if( T != NULL)
	{
		MakeEmpty(T->left);
		MakeEmpty(T->right);
		free(T);
	}
	return NULL;
}

/*
 * 查找 可以像普通二叉查找树一样的进行,所以耗费O(log n)时间,因为AVL树总是保持平衡的。
 * 不需要特殊的准备,树的结构不会由于查找而改变。(这是与伸展树查找相对立的,它会因为查找而变更树结构。)
 */
Position Find(ElementType x,AvlTree T)
{
	if(T == NULL)
		return NULL;
	if(x < T->data)
		return Find(x,T->left);
	else if(x > T->data)
		return Find(x,T->right);
	else
		return  T;
}
/*
 *   FindMax,FindMin 查找最大和最小值,
 */
Position FindMin(AvlTree T)
{
	if(T == NULL)
		return NULL;
	if( T->left == NULL)
		return T;
	else
		return FindMin(T->left);
}
Position FindMax(AvlTree T)
{
	if(T != NULL)
		while(T->right != NULL)
			T=T->right;
	return T;
}
/*
 *  返回节点的高度
 */
static int Height(Position P)
{
	if(P == NULL)
		return -1;
	else
		return P->Height;
}
static int Max(int h1,int h2)
{
	return h1 > h2 ?h1:h2;
}
/*
 *  此函数用于k2只有一个左孩子的单旋转,
 *  在K2和它的左孩子之间旋转,
 *  更新高度,返回新的根节点
 */
static Position SingleRotateWithLeft(Position k2)     // LL旋转
{
	Position k1;
	k1=k2->left;
	k2->left=k1->right;
	k1->right=k2;
	// 因为比较的是左右孩子的高度,所以求父节点的高度要加1
	k2->Height=Max(Height(k2->left),Height(k2->right)) + 1;
	k1->Height=Max(Height(k1->left),Height(k2->right)) + 1;
	return k1;
}
/*
 *  此函数用于k1只有一个右孩子的单旋转,
 *  在K1和它的右孩子之间旋转,
 *  更新高度,返回新的根节点
 */
static Position SingleRotateWithRight(Position k1)  // RR旋转
{
	Position k2;
	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;
}
/*
 * 此函数用于当 如果 k3有一个左孩子,以及
 * 它的左孩子又有右孩子,执行这个双旋转
 * 更新高度,返回新的根节点
 */
static Position DoubleRotateLeft(Position k3)    // LR旋转
{
	//在 k3 的左孩子,执行右侧单旋转
	k3->left=SingleRotateWithRight(k3->left);
	// 再对 k3 进行 左侧单旋转
	return SingleRotateWithLeft(k3);
}
/*
 * 此函数用于当 如果 k4有一个右孩子,以及
 * 它的右孩子又有左孩子,执行这个双旋转
 * 更新高度,返回新的根节点
 */
static Position DoubleRotateRight(Position k4)    // RL旋转
{
	//在 k4 的右孩子,执行左侧单旋转
	k4->right = SingleRotateWithLeft(k4->right);
	// 再对 k4 进行 右侧单旋转
	return SingleRotateWithRight(k4);
}
/*
 *  向AVL树插入可以通过如同它是未平衡的二叉查找树一样把给定的值插入树中,
 *  接着自底向上向根节点折回,于在插入期间成为不平衡的所有节点上进行旋转来完成。
 *  因为折回到根节点的路途上最多有1.5乘log n个节点,而每次AVL旋转都耗费恒定的时间,
 *  插入处理在整体上耗费O(log n) 时间。
 */
AvlTree  Insert(ElementType x,AvlTree T)
{
	//如果T不存在,则创建一个节点树
	if(T == NULL)
	{
		T = (AvlTree)malloc(sizeof(struct AvlNode));
		{
			T->data = x;
			T->Height = 0;
			T->left = T->right = NULL;
		}
	}
	// 如果要插入的元素小于当前元素
	else if(x < T->data)
	{
		//递归插入
		T->left=Insert(x,T->left);
		//插入元素之后,若 T 的左子树比右子树高度 之差是 2,即不满足 AVL平衡特性,需要调整
		if(Height(T->left) - Height(T->right) == 2)
		{
			//把x插入到了T->left的左侧,只需 左侧单旋转
			if(x < T->left->data)
				T = SingleRotateWithLeft(T);       // LL旋转
			else
				// x 插入到了T->left的右侧,需要左侧双旋转
				T =  DoubleRotateLeft(T);          // LR旋转
		}
	}
	// 如果要插入的元素大于当前元素
	else if(x > T->data)
	{
		T->right=Insert(x,T->right);
		if(Height(T->right) - Height(T->left) == 2)
		{
			if(x > T->right->data)
				T = SingleRotateWithRight(T);     //RR 旋转
			else
				T =  DoubleRotateRight(T);        //RL旋转
		}
	}
	T->Height=Max(Height(T->left),Height(T->right)) + 1;
	return T;
}
/*
 *  对单个节点进行的AVL调整
 */
AvlTree Rotate(AvlTree T)
{

	if(Height(T->left) - Height(T->right) == 2)
	{
		if(Height(T->left->left) >= Height(T->left->right))
			T = SingleRotateWithLeft(T);  // LL旋转
		else
			T =  DoubleRotateLeft(T);     // LR旋转
	}
	if(Height(T->right) - Height(T->left) == 2)
	{
		if(Height(T->right->right) >= Height(T->right->left))
			T = SingleRotateWithRight(T);  // RR旋转
		else
			T =  DoubleRotateRight(T);     // RL旋转
	}
	return T;
}
/*
 * 首先定位要删除的节点,然后用该节点的右孩子的最左孩子替换该节点,
 * 并重新调整以该节点为根的子树为AVL树,具体调整方法跟插入数据类似
 * 删除处理在整体上耗费O(log n) 时间。
 */
AvlTree  Delete(ElementType x,AvlTree T)
{
	if(T == NULL)
		return NULL;
	if(T->data == x)           // 要删除的 x 等于当前节点元素
	{
		if(T->right == NULL )  // 若所要删除的节点 T 的右孩子为空,则直接删除
		{
			AvlTree tmp = T;
			T = T->left;
			free(tmp);
		}
		else                 /* 否则找到 T->right 的最左儿子代替 T */
		{
			AvlTree tmp = T->right;
			while(tmp->left)
				tmp=tmp->left;
			T->data = tmp->data;
			/* 对于替代后的T 即其字节点进行调整*/
			T->right = Delete(T->data,T->right);
			T->Height = Max(Height(T->left),Height(T->right))+1;
		}
		return T;
	}
	else if(x > T->data)                       // 要删除的 x 大于当前节点元素,在T的右子树中查找删除
	{
		T->right=Delete(x,T->right);
	}
	else                                       // 要删除的 x 小于当前节点元素,在T的左子树中查找删除
	{
		T->left=Delete(x,T->left);
	}
	/*
	 *   当删除元素后调整平衡
	 */
	T->Height=Max(Height(T->left),Height(T->right)) + 1;
	if(T->left != NULL)
		T->left = Rotate(T->left);
	if(T->right != NULL)
		T->right = Rotate(T->right);
	if(T)
		T=Rotate(T);
	return T;
}
/*
 * 返回当前位置的元素
 */
ElementType Retrieve(Position P)
{
	return P->data;
}
/*
 * 遍历输出
 */
void Display(AvlTree T)
{
	static int n=0;
	if(NULL != T)
	{
		Display(T->left);
		printf("[%d] ndata=%d \n",++n,T->data);
		Display(T->right);
	}
}

AvlTree.c

#include"Head.h"
#define N 15
int main(void) {
    AvlTree T=NULL;
    int i;
    int j = 0;
    T = MakeEmpty( NULL );
    for( i = 0; i < N; i++, j = ( j + 7 ) % 50 )
    {
    	printf("j=%d \n",j);
        T = Insert( j, T );
    }
    puts("插入 4 \n");
    T = Insert( 4, T );
    Display(T);
   for( i = 0; i < N; i += 2 )
   {
	   printf("delelte: %d \n",i);
        T = Delete( i, T );
   }
   printf("detele:\n");
   printf("height=%d \n",T->Height);
   Display(T);

    printf( "Min is %d, Max is %d\n", Retrieve( FindMin( T ) ),
               Retrieve( FindMax( T ) ) );
	return EXIT_SUCCESS;
}

此外,还可参见   http://blog.youkuaiyun.com/w397090770/article/details/8119616

AVL是一种自平衡的二叉查找,它确保了的高度始终保持在对数级别,从而保证了查找、插入和删除操作的时间复杂度为O(log n)。这种数据结构以它的发明者G.M. Adelson-Velsky和E.M. Landis的名字命名[^1]。 ### AVL的定义 - **平衡因子**:每个节点都有一个平衡因子,它是该节点左子的高度减去右子的高度。对于AVL来说,所有节点的平衡因子只能是-1, 0或1。 - **空**:如果T是空,则它自然是一个AVL。 - **非空**:若T不是空,则其左右子TL和TR都必须是AVL,并且对于任意节点,|HL - HR| ≤ 1(其中HL和HR分别表示左子和右子的高度)。 ### AVL的操作 当进行插入或删除操作时,可能会破坏AVL的平衡性,这时需要通过旋转来重新恢复平衡: - **单旋转**: - 左单旋(Single Rotate with Left)用于处理左孩子的左子过高。 - 右单旋(Single Rotate with Right)用于处理右孩子的右子过高。 - **双旋转**: - 左右双旋(Double Rotate with Left)用于处理左孩子的右子过高的情况。 - 右左双旋(Double Rotate with Right)用于处理右孩子的左子过高的情况。 这些旋转操作可以保持AVL的性质不变,并且能够快速地调整的结构以维持平衡。 ### AVL的实现 下面是一个简单的C语言代码示例,展示了如何定义AVL的节点以及实现基本的插入操作。这个例子中包含了必要的函数声明和一些核心逻辑。 ```c #include <stdio.h> #include <stdlib.h> // 定义AVL节点结构体 typedef struct AvlNode { int element; struct AvlNode *left; struct AvlNode *right; int height; // 节点的高度 } *AvlTree, *Position; // 获取两个整数中的较大者 int max(int a, int b) { return (a > b) ? a : b; } // 计算给定节点的高度 static int Height(AvlTree T) { if (T == NULL) return -1; // 空节点高度为-1 else return T->height; } // 单旋转 - 左左情况 AvlTree singlerotatewithLeft(AvlTree K2) { Position 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; // 新根 } // 单旋转 - 右右情况 AvlTree singlerotatewithRight(AvlTree K2) { Position 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; // 新根 } // 双旋转 - 左右情况 AvlTree doublerotatewithLeft(AvlTree K3) { K3->left = singlerotatewithRight(K3->left); return singlerotatewithLeft(K3); } // 双旋转 - 右左情况 AvlTree doublerotatewithRight(AvlTree K3) { K3->right = singlerotatewithLeft(K3->right); return singlerotatewithRight(K3); } // 插入新元素到AVLAvlTree Insert(int x, AvlTree T) { if (T == NULL) { // 如果为空,创建新节点 T = (AvlTree)malloc(sizeof(struct AvlNode)); if (T == NULL) printf("Out of space!!!"); else { T->element = x; T->left = T->right = NULL; T->height = 0; // 新叶节点高度为0 } } 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; } ``` 上述代码提供了AVL的基本框架,包括节点定义、插入操作及必要的旋转方法。实际应用中可能还需要添加更多的功能,如删除节点、查找特定值等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值