平衡二叉树(AVL树)

基本概念

  • ASL:一颗搜索树查找元素的平均查找次数。
  • 平衡因子(BF):BF(T) = hL–hR。其中hL,hR分别为左右子树高度。

定义

平衡二叉树也是一颗搜索树,它的ASL相对来说会小一些。满足以下特性:

  • 是一颗空树,或者任一结点左、右子树高度差的绝对值不超过1,即|BF(T)|≤1。
/*  AVL树结点定义 */
typedef struct node* AVLTree;
struct node 
{
	int data;
	int height;/* 比BST多一个高度 */
	AVLTree left;
	AVLtree right;
};

如下图就是一颗AVL树:
在这里插入图片描述


操作

AVL树的一些基本操作如插入、删除等与BST类似。下面主要介绍如何将一颗BST调整为AVL树。

在插入一个元素的时候,有可能会把一颗AVL树的平衡破坏,这时候就要调整。分下面4种情况:

  1. 右单旋
    在这里插入图片描述
  • 特点:不平衡的“发现者”是1,“麻烦节点”3在发现者的右子树的右子树上,因而叫RR插入,需要RR旋转(右单旋)。
  • 调整:在这里插入图片描述
    将被破坏结点的右子树提到上面去,即将B提到A的上面。B的右子树依然挂在B的右边,而B的左子树,由于比B小、比A大,所以调整之后挂在A的右子树上。

代码如下:

/*  右单旋  */
AVLTree SingleRightRotation(AVLTree A)
{
	AVLTree B = A->right;//B在A的右子树上,先存起来
	A->right = B->left;//把B的左子树挂在A的右子树上
	B->left = A;//把B提到A的上面去
	
	/*  由于现在A是B的左子树,先更新A的树高、再更新B的树高  */
	A->height = max(getheight(A->left), getheight(A->right)) + 1;
	B->height = max(getheight(B->right), A->height) + 1;
	return B;
}
  1. 左单旋
    分析类似,调整:将B提到A的上面,把B的右子树挂在A的左子树上
/*  左单旋  */
AVLTree SingleLeftRotation(AVLTree A)
{
	AVLTree B = A->left;
	A->left = B->right;
	B->right = A;
	
	A->height = max(getheight(A->left), getheight(A->right)) + 1;
	B->height = max(getheight(B->left), A->height)) + 1;
	return B;
}
  1. 左右双旋
    在这里插入图片描述
  • 特点:不平衡的“发现者”是8,“麻烦节点"7在8的左子树的右子树上,因而叫LR插入,需要LR旋转。
  • 调整:在这里插入图片描述
    代码如下:
/*  左右双旋  */
AVLTree DoubleLeftRightRotation(AVLTree A)
{
	/* 左两次单旋 :
	   先将B、C做右单旋,此时A的左孩子是C,C的左孩子是B,C被返回;
	   再将A、C做左单旋,C被返回 */

	A->left = SingleRightRotation(A->left);
	return SingleLeftRotation(A);
}
  1. 右左双旋

直接给出代码:

/*  右左双旋  */
AVLTree DoubleRightLeftRotation(AVLTree A)
{
	A->right = SingLeftRotation(A->right);
	return SingRightRotation(A);
}

模板

最后给出插入元素的过程中将树调整为AVL树的代码:

#include <stdio.h>
#include <stdlib.h>
typedef struct node* avl;
struct node 
{
	int data;
	int height;
	avl left;
	avl right;
};
int max(int a, int b)
{
	return a > b ? a : b;
}
int getheight(avl t)
{
	if(!t) return -1;/* 结点为空时,返回-1 */
	return t->height;
}
avl sl(avl A)/* 左单旋 */
{
	avl B = A->left;
	A->left = B->right;
	B->right = A;
	A->height = max(getheight(A->left), getheight(A->right)) + 1;
	B->height = max(getheight(B->left), A->height) + 1;
	return B;
}
avl sr(avl A)/* 右单旋 */
{
	avl B = A->right;
	A->right = B->left;
	B->left = A;
	A->height = max(getheight(A->left), getheight(A->right)) + 1;
	B->height = max(getheight(B->right), A->height) + 1;
	return B;
}
avl dlr(avl A)/* 左右双旋 */
{
	A->left = sr(A->left);
	return sl(A);
}
avl drl(avl A)/* 右左双旋 */
{
	A->right = sl(A->right);
	return sr(A);
}
avl Insert(avl t, int x)/* 插入元素并调整树 */
{
	if(!t)
	{
		t = (avl)malloc(sizeof(struct node));
		t->data = x;
		t->height = 0;
		t->left = t->right = NULL;
	}/* 找到了插入位置,生成结点,存入信息 */
	else 
		if(x < t->data)
		{
			/* 插入左子树 */		
			t->left = Insert(t->left, x);
			/*  左子树的高度已经比右子树高2了,即需要左旋  */
			if(getheight(t->left) - getheight(t->right) == 2)
			{
				/*  要分情况看插在了哪边  */
				
				if(x < t->left->data)/* 左子树的左子树,即左单旋 */
					t = sl(t);
				else /* 左子树的右子树,即左右双旋 */
					t = dlr(t);
			}
		}
		else if(x > t->data)
		{
			/* 插入右子树 */
			t->right = Insert(t->right, x);
			/*  右子树的高度已经比左子树高2了,即需要右旋  */
			if(getheight(t->left) - getheight(t->right) == -2)
			{
				if(x > t->right->data)/* 右子树的右子树,即右单旋 */
					t = sr(t);
				else /* 右子树的左子树,即右左双旋 */
					t = drl(t);
			}
		}

	/* 最后,插入元素就需要更新树高 */
	t->height = max(getheight(t->left), getheight(t->right)) + 1;
	return t;
}		
void preorder(avl t)
{
	if(!t) return ;
	printf("BF(%d) = %d\n", t->data, getheight(t->left) - getheight(t->right));
	preorder(t->left);
	preorder(t->right);
} 		
int main()
{
	avl t = NULL;
	int n, i, x;
	scanf("%d", &n);
	for(i=0; i<n; i++) 
	{
		scanf("%d", &x);
		t = Insert(t, x);
	}
	preorder(t);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值