C语言 数据结构与算法---AVL-平衡二叉树

一. 平衡二叉树的概念

1.为什么需要平衡二叉树

在这里插入图片描述
为了避免这种情况发生,提高效率

性质:

  • 一颗平衡二叉树或者空树,或者具有下列性质的二叉排序树
    • 左子树与右子树的高度差的绝对值小于或等于1
    • 左子树与右子树也是平衡二叉排序树

2.平衡因子

BF = 左右子树高度差 = 左子树的深度-右子树深度
(BF = -1,0,1)

平衡二叉树,也被称为高度平衡树。相比于”二叉查找树”,它的特点是:AVL树中任何节点的两个子树的高度最大差别为1。
在这里插入图片描述
在这里插入图片描述

二. 平衡二叉树的分析与调整

1.分析

当我们在一个平衡二叉排序树上插入一个结点时,有可能导致失衡,即出现平衡因子绝对值大于一的结点
在这里插入图片描述

2.平衡调整的四种类型

在这里插入图片描述
在这里插入图片描述
原则:

  • 降低高度
  • 保持二叉排序树的性质

1. LL型

在这里插入图片描述

  • B结点带左子树一起上升
  • A结点成为B的右孩子
  • 原来B结点的右子树作为A的左子树

在这里插入图片描述

2. RR型

在这里插入图片描述

  • B结点带右子树一起上升
  • A结点成为B的左孩子
  • 原来B结点的左子树作为A的右子树

在这里插入图片描述
图例:
在这里插入图片描述
调整后:
在这里插入图片描述

3. LR型

在这里插入图片描述

  • C结点穿过A、B结点上升
  • B结点成为C的左孩子
    A结点成为C的右孩子
  • 原来C结点的子树作为B的子树
    原来C结点的子树作为A的子树

C原来的左子树变成上升后的左子树的右子树;原来的右子树变成上升后的右子树的左子树
在这里插入图片描述

4. RL型

在这里插入图片描述
图例:
在这里插入图片描述
调整后:
在这里插入图片描述
9原来的左子树变成了上升后的左子树的右子树

三. 平衡二叉树的实现

1. 平衡二叉树的结构

与二叉树大致相同,多了平衡因子

typedef struct BinNode
{
	int data;
	int bf;    //平衡因子
	struct BinNode* lchild;
	struct BinNode* rchild;
}BinNode, * BinTree;

2. 平衡二叉树的调整

右旋:

//传入需要做右璇处理的子树的根结点
//处理完毕后 P 指向新的子树的根结点
void R_Rotate(BinTree * P)
{
	BinTree L;
	L = (*P)->lchild;
	(*P)->lchild = L->rchild;
	L->rchild = (*P);
	*P = L;
}

左旋:

void L_Rotate(BinTree* P)
{
	BinTree R;
	R = (*P)->rchild;
	(*P)->rchild = R->lchild;
	R->lchild = (*P);
	*P = R;
}

左子树平衡旋转代码:

//传进来需要左平衡处理的结点,结束后,T指向新的子树的根结点
//既然已经需要做左平衡处理,那么它的BF值肯定为正,只要判断它的左子树的BF值进行了
void LeftBalance(BinTree* T)
{
	BinTree L, Lr;
	L = (*T)->lchild;
	//检查左子树的平衡度
	switch (L->bf)
	{
	case 1://如果左边高,那么BF值都为正,只需要做右璇处理就行了
		(*T)->bf = L->bf = 0;//处理完毕后,BF值都为0
		R_Rotate(T);
		break;
	case -1://如果左子树的BF值为负,说明符号为负,需要先把符号变为相同,再进行右璇处理
		Lr = L->rchild;
		//后面会进行双旋操作
		switch (Lr->bf)//通过判断Lr的bf值,修改T和T的左孩子的平衡因子
		{
		case 1://如果L的右子树是左高,那么双旋过后,T应该就是右高
			(*T)->bf = -1;
			L->bf = 0;
			break;
		case 0://如果等高,那么双旋过后应该是都是等高
			(*T)->bf = L->bf = 0;
			break;
		case -1://如果是右高,那么双旋过后,L应该是左高
			(*T)->bf = 0;
			L->bf = 1;
			break;
		}
		Lr->bf = 0;
		L_Rotate(&(*T)->lchild);//对T的左子树进行左旋
		R_Rotate(T);//对T进行右旋,整个是个双旋操作
	}
}

右子树平衡旋转代码:

void RightBalance(BinTree* T)
{
	BinTree R, Rl;
	R = (*T)->rchild;
	switch (R->bf)
	{
	case -1:
		(*T)->bf = R->bf = 0;
		break;
	case 1:
		Rl = R->lchild;
		switch (Rl->bf)
		{
		case 1:
			(*T)->bf = 0;
			R->bf = -1;
		case 0:
			(*T)->bf = R->bf = 0;
			break;
		case -1:
			(*T)->bf = 1;
			R->bf = 0;
		}
		Rl->bf = 0;
		R_Rotate(&(*T)->rchild);
		L_Rotate(T);
	}
}

3. 平衡二叉树的插入

//若在二叉排序树中不存在关键字为e的结点,则将e插入
//插入的过程通过旋转操作保持树的平衡
//插入成功返回true,否则返回false
//taller是一个判断二叉树的高度是否增加的值
//bf:左高为1 ;右高为-1 ;等高为0
int InsertAVL(BinTree* T, int e, int* taller)
{
	if (!*T)//找到位置,插入结点
	{
		*T = (BinTree)malloc(sizeof(BinNode));
		(*T)->data = e;
		(*T)->lchild = (*T)->rchild = NULL;
		(*T)->bf = 0;
		*taller = true;
	}
	else
	{
		if (e == (*T)->data)//树中有关键字和e相同的结点,不进行插入操作
		{
			*taller = false;
			return false;
		}
		if (e < (*T)->data)//在左子树中进行搜索
		{
			if (!InsertAVL(&(*T)->lchild, e, taller))//在左子树中递归,若最终返回false,则该步返回false
			{
				return false;
			}
			if (*taller)//这个结点插入到了左子树(到这步的时候,实际上已经从InsertAVL返回了)
			{
				switch ((*T)->bf)//判断T的平衡因子,这个T的bf值仍然是插入操作之前的值
				{
				case 1://原来是左子树高,现在左子树又增加一个结点,左子树偏高,做左子树平衡处理
					LeftBalance(T);
					*taller = false;//平衡完后重置标志变量
					break;
				case 0://原来是等高,现在左子树加了,说明左子树高了
					(*T)->bf = 1;
					*taller = true;//加入后,仍然左高,那么高度是真的增加了
					break;
				case -1://原来右边高,现在左边增加了一个结点,等高了
					(*T)->bf = 0;
					*taller = false;//因为是等高,所以重置标志变量
					break;
				}
			}
		}
		else//在右子树中搜素,过程与上面差不多
		{
			if (!InsertAVL(&(*T)->rchild, e, taller))
			{
				return false;
			}
			if (*taller)
			{
				switch ((*T)->bf)
				{
				case 1://原来左边高,现在等高
					(*T)->bf = 0;
					*taller = false;
					break;
				case 0://原来等高,现在右边高
					(*T)->bf = -1;
					*taller = true;
					break;
				case -1://原来右边高,现在又加了一个,需要左右平衡处理
					RightBalance(T);
					*taller = false;
					break;
				}
			}
		}
	}
}
发布了37 篇原创文章 · 获赞 30 · 访问量 1117

猜你喜欢

转载自blog.csdn.net/myjess/article/details/104538797