0
点赞
收藏
分享

微信扫一扫

STL容器之deque

九月的栩 2023-05-14 阅读 121
数据结构

目录

AVL树的概念

旋转

单旋转

双旋转

小结

AVL树的视频演示

代码示例


AVL树的概念

AVL树是一种自平衡的平衡二叉查找树,它是一种高效的数据结构,可以在插入和删除节点时保持树的平衡,从而保证树的操作时间复杂度能够达到O(log n)。

所谓平衡就是保证每个节点的左右子树的高度差不能超过1。例如

bd8189489ce848318a977ea8ebd479f6.jpeg

对AVL树进行插入或删除操作时,可能会导致某些节点的高度差超过1,即不再平衡。这时就需要进行旋转操作来恢复AVL树的平衡。所以,AVL树的核心内容就是旋转。

旋转

一般来讲,我们将旋转类型分为两大类。左-左、右-右类型的为单旋转,左-右、右-左类型的为双旋转。下面是这四种旋转的操作方式。

单旋转

 具体的图解如下: 

5cb4b4d6000447a7b69892d8ff0b10da.png

非原创图,原图出处:CSDN 喵了个呜s

双旋转

具体图解如下:

a3965d75ae3d472785c49be89acffefd.png

非原创图,原图出处:CSDN 喵了个呜s

小结

单旋转的过程可以概况为如下的三个步骤(以下图为模型,以单左旋为例):

        1、让k2原本指向k1的指针现在指向k1内侧的节点

        2、让k1原本指向内侧的指针现在指向k2

        3、让原来指向k2的指针现在改为指向k1,并更新各节点的高度

74a2c69e473f46ec9a0b9d2803ffca99.png


双旋转其实就是两次单旋转,先将内侧的节点通过单旋转“移出来”到外侧。然后再用一次单旋转,最终成为我们想要的平衡状态。

AVL树的视频演示

AVL树动画演示

也可以自己尝试:

AVL Tree Visualzation (usfca.edu)https://www.cs.usfca.edu/~galles/visualization/AVLtree.html

代码示例

#include<stdio.h>
/*max的头文件stdlib.h */
#include<stdlib.h>

typedef char ElementType;
typedef struct AvlTreeNode
{
	ElementType Data; //暂定节点内容只有单个字符
	int Height;
	struct AvlTreeNode* Left;
	struct AvlTreeNode* Right;
}AvlTree; 

int Height(AvlTree* Node)
{
	if (Node == NULL)
		return -1;
	return Node->Height;
}
AvlTree* SingleLeftRotate(AvlTree* k2) //单左旋,LL旋转(k2的由来详见数据结构与算法分析P94)
{
	//旋转节点
	AvlTree* 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), Height(k1->Right)) + 1;
	//返回
	return k1;
}
AvlTree* SingleRightRotate(AvlTree* k2) //单右旋,RR旋转(k2的由来详见数据结构与算法分析P94)
{
	//旋转节点
	AvlTree* 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->Left), Height(k1->Right)) + 1;
	//返回
	return k1;
}
AvlTree* DoubleLRRotate(AvlTree* k3) //双左右旋,LR旋转(k3的由来详见数据结构与算法分析P95)
{
	/*一次双旋转等于两次单旋转。
	可以理解为先将需要旋转的移至同一方向(即左左、右右这种),然后再用单旋转的方式处理*/
	k3->Left = SingleRightRotate(k3->Left); 
	return SingleLeftRotate(k3);  
}
AvlTree* DoubleRLRotate(AvlTree* k3) //双右左旋,RL旋转(k3的由来详见数据结构与算法分析P95)
{
	/*一次双旋转等于两次单旋转。
	可以理解为先将需要旋转的移至同一方向(即左左、右右这种),然后再用单旋转的方式处理*/
	k3->Right = SingleLeftRotate(k3->Left);
	return SingleRightRotate(k3);
}
AvlTree* InsertElement(AvlTree** root, ElementType data)
{
	//走到空节点(即插入位置),执行插入操作
	if ((*root) == NULL)
	{
		//开辟空间并赋值
		*root = (AvlTree*)calloc(1, sizeof(AvlTree));
		//成功开辟空间
		if ((*root) != NULL)
			(*root)->Data = data; 
		//开辟空间失败
		else	
			puts("heap area is full!");
	} 
	//根节点无内容(值为0),说明为空树,则直接将data插入到根节点
	else if ((*root)->Data == 0) 
	{
		(*root)->Data = data;
	}
	//data比节点内容小,在左侧插入
	else if (data < (*root)->Data) 
	{
		//向左走,并更新左子树内容
		(*root)->Left = InsertElement(&(*root)->Left, data);	
		//判断是否需要旋转
		if (Height((*root)->Left) - Height((*root)->Right) == 2)
		{
			//如果data小于左子树的data,说明是data插入到左子树的左节点,符合单旋转的情况(3个节点都在左侧)
			if (data < (*root)->Left->Data) 
				*root = SingleLeftRotate(*root); //左侧单旋转,并更新节点内容
			//如果data不小于左子树的data,说明是插入到左子树的右节点,是LR型的双旋转情况
			else
				*root = DoubleLRRotate(*root); //左右双旋转,并更新节点内容
		}
	}
	//data比节点内容大,在右侧插入
	else if (data > (*root)->Data) 
	{
		//向右走,并更新左子树内容
		(*root)->Right = InsertElement(&(*root)->Right, data); 
		//判断是否旋转
		if (Height((*root)->Right) - Height((*root)->Left) == 2) 
		{
			//如果data大于右子树的data,说明是data插入到右子树的右节点,符合单旋转的情况(3个节点都在右侧)
			if (data > (*root)->Right->Data) 
				*root = SingleRightRotate(*root);  //右侧单旋转,并更新节点内容
			//如果data不大于右子树的data,说明是插入到右子树的左节点,是RL型的双旋转情况
			else
				*root = DoubleRLRotate(*root);
		}
	}
	//data与节点内容值相同
	else {  /*暂定如果插入的元素内容相同,则什么都不做*/  }
	//最后更新节点高度
	(*root)->Height = max(Height((*root)->Left), Height((*root)->Right)) + 1; 
	//返回
	return *root;
}

int main()
{
	AvlTree* root = (AvlTree*)calloc(1, sizeof(AvlTree));
	InsertElement(&root, 'n');
	InsertElement(&root, 'g');
	InsertElement(&root, 'u');
	InsertElement(&root, 'e');
	InsertElement(&root, 'd');
	InsertElement(&root, 'f');
	InsertElement(&root, 'h');
	InsertElement(&root, 'a');
	InsertElement(&root, 'b');
	InsertElement(&root, 'c');
	InsertElement(&root, 'i');
	InsertElement(&root, 'j');
	InsertElement(&root, 'k');
	InsertElement(&root, 'z');
	puts("insert test over.");
	puts("*************************************");
	return 0;
}




举报

相关推荐

0 条评论