数据结构——平衡二叉树(AVL树)(C语言实现)

本文介绍了一个基于二叉搜索树下的平衡二叉树程序。重点讲解了如何判断平衡因子不平衡及何时修改平衡因子,通过递归实现插入数据并保持树的平衡。

此程序是基于二叉搜索树下的平衡二叉树
我觉得大家对于二叉树不平衡时的四种情况,以及要怎么调整为平衡二叉树应该都是明白的,大家最疑惑的应该是运行程序时如何判断平衡因子不平衡以及何时修改平衡因子。所以我在此着重讲解一下这个问题,此程序借助递归实现的,当插入的数据小于根节点的数据时,递归进入左子树,反之进入右子树,直到某一刻达到递归终止条件时,再往回返,所以可以根据返回来的时候可以根据确定此时处于在左子树还是右子树,再结合未修改之前的平衡因子判断该做出什么调整(注意插入数据后,往回返的时候才判断并修改平衡因子,并且先判断应怎么调整,就那四种情况,调整完之后,再修改平衡因子)。
文字描述就这么多了,具体的直接请各位看代码,注释很详细。

#include<stdio.h>
/*
插入数据时按照搜索二叉树插入
往回递归的时候修改平衡因子
发现不平衡时再调整二叉树
再修改调整后的平衡因子
不平衡时有四种情况:
从被破坏的节点往下看
分为两大类:
左边被破坏->细分为左左,左右
右边被破坏->细分为右左,右右
*/
#define LH 1//左树高
#define EH 0//一样高或空树
#define RH -1//右树高
#define MAX 10
struct avltree
{
	int date;
	int flag;
	struct avltree* lchildren;
	struct avltree* rchildren;
};

int insertelement(struct avltree** p, int in_date,int *tall);
void Rblance(struct avltree** p);
void Lblance(struct avltree** p);
void R_turn(struct avltree** p);
void L_turn(struct avltree** p);
void print(struct avltree* p);

 
int insertelement(struct avltree** p,int in_date,int *tall)
{
	if (!(*p))//空树创建第一个节点或者二叉树已经遍历到叶子节点
	{
		*p = malloc(sizeof(struct avltree));
		(*p)->date = in_date;
		(*p)->flag = 0;
		(*p)->lchildren = NULL;
		(*p)->rchildren = NULL;
		*tall = 1;
		return 1;
	}
	else if ((*p)->date == in_date)//发现树中已存在此元素
	{
		*tall = 0;
		return 0;
	}
	else if ((*p)->date > in_date)//插入元素比此节点数据小
	{
		if (!(insertelement(&((*p)->lchildren), in_date, tall)||*tall))//递归进左树,并判断是否插入成功
		{
			*tall = 0;
			return 0;//插入元素失败或插入成功但并没有使树增高
		}
		else if (*tall)
		{
			//子树增高
			//因为是从左子树递归回来的,所以是左子树增高
			//结合此节点原平衡因子,判断此树现在的平衡因子,并修改
			if ((*p)->flag == RH)
			{//原此节点右子树高,现左子树增高,两边平衡
				(*p)->flag = 0;
				*tall = 0;
			}
			else if ((*p)->flag == EH)
			{//原此节点两边等高,现左子树增高,此树增高
				(*p)->flag = LH;
				*tall = 1;
			}
			else
			{//原此节点左子树高,现左子树增高,此树平衡破坏
				//平衡此树
				Lblance(p);
				*tall = 0;
			}
			return 1;
		}

	}
	else//插入元素比此节点数据大
	{
		if(!(insertelement(&((*p)->rchildren), in_date, tall)||*tall))//递归进右树,并判断是否插入成功
		{
			*tall = 0;
			return 0;//插入元素失败或插入成功但并没有使树增高
		}
		else if (*tall)
		{
			//子树增高
			//因为是从左子树递归回来的,所以是左子树增高
			//结合此节点原平衡因子,判断此树现在的平衡因子,并修改
			if ((*p)->flag == LH)
			{//原此节点右子树高,现左子树增高,两边平衡
				(*p)->flag = 0;
				*tall = 0;
			}
			else if ((*p)->flag == EH)
			{//原此节点两边等高,现左子树增高,此树增高
				(*p)->flag = RH;
				*tall = 1;
			}
			else
			{//原此节点左子树高,现左子树增高,此树平衡破坏
				//平衡此树
				Rblance(p);
				*tall = 0;
			}
			return 1;
		}

	}

}

void Lblance(struct avltree** p)
{//两种情况:左左,左右
	//因为是左子树增高,所以直接看现在左子树的平衡因子
	//LH为左左情况
	//RH为左右情况
	
	if ((*p)->lchildren->flag == LH)//左左,左旋一次
	{
		L_turn(p);
		(*p)->flag = 0;
		(*p)->rchildren->flag = 0;//..修改平衡因子
	}
	else if ((*p)->lchildren->flag == RH)//左右,先进行一次左子树的右旋,再进行一次根节点的左旋
	{//左右的情况有点特殊,平衡后的平衡因子有两种情况
		//左右的情况是,根节点的左孩子的右孩子(右孙子)的子树导致不平衡
		//而不平衡点可能是右孙子的左孩子或右孩子导致的,这会导致平衡后的平衡因子的不同
		//所以在修改前需要先判断是右孙子的哪个孩子破坏平衡-->看右孙子的平衡因子是LH还是RH
		int judge = (*p)->lchildren->rchildren->flag;
		R_turn(&(*p)->lchildren);
		L_turn(p);
		//..修改平衡因子
		//如果judge是LH,则平衡后的根节点平衡因子为零,左孩子平衡因子为零,右孩子平衡因子为-1
		if (judge == LH)
		{
			(*p)->flag = 0;
			(*p)->lchildren->flag = 0;
			(*p)->rchildren->flag = -1;
		}
		//如果judge是RH,则平衡后的根节点平衡因子为零,左孩子平衡因子为1,右孩子平衡因子为0
		else if (judge == RH)
		{
			(*p)->flag = 0;
			(*p)->lchildren->flag = 1;
			(*p)->rchildren->flag = 0;
		}
	}
}
void Rblance(struct avltree** p)
{

	//两种情况:右右,右左
	//因为是右子树增高,所以直接看现在右子树的平衡因子
	//LH为右左情况
	//RH为右右情况

	if ((*p)->rchildren->flag == RH)//右右,右旋一次
	{
		R_turn(p);
		(*p)->flag = 0;//修改平衡因子
		(*p)->lchildren->flag = 0;
	}
	else if ((*p)->rchildren->flag == LH)//右左,先进行一次右子树的左旋,在进行一次根节点的右旋
	{
		int judge = (*p)->rchildren->lchildren->flag;
		L_turn(&(*p)->rchildren);
		R_turn(p);
		//修改平衡因子
		if (judge == LH)
		{
			(*p)->flag = 0;
			(*p)->lchildren->flag = 0;
			(*p)->rchildren->flag = -1;
		}
		//如果judge是RH,则平衡后的根节点平衡因子为零,左孩子平衡因子为1,右孩子平衡因子为0
		else if (judge == RH)
		{
			(*p)->flag = 0;
			(*p)->lchildren->flag = 1;
			(*p)->rchildren->flag = 0;
		}


	}
}
void L_turn(struct avltree** p)//左旋,左边节点高向右旋转
{
	struct avltree* change = *p;
	*p = (*p)->lchildren;
	if ((*p)->rchildren)
	{
		change->lchildren = (*p)->rchildren;
		(*p)->rchildren = change;
	}
	else
	{
		(*p)->rchildren = change;
		change->lchildren = NULL;
	}
}
void R_turn(struct avltree** p)//右旋,右边节点高向左旋转
{
	struct avltree* change = *p;
	*p = (*p)->rchildren;
	if ((*p)->lchildren)
	{
		change->rchildren = (*p)->lchildren;
		(*p)->lchildren = change;
	}
	else
	{
		(*p)->lchildren = change;
		change->rchildren = NULL;
	}
}

void print(struct avltree* p)
{
	if (!p)
	{
		return 0;
	}
	if (p->lchildren)
	{
		print(p->lchildren);
	}
	printf("%d ", p->date);
	if (p->rchildren)
	{
		print(p->rchildren);
	}
	

}

int main()
{
	int in_date;
	int tall = 0;
	struct avltree* tree = NULL;
	printf("请输入%d个数据\n", MAX);
	for (int i = 0; i < MAX; i++)
	{
		scanf("%d", &in_date);
		insertelement(&tree, in_date, &tall);
	}
	print(tree);
	system("pause");
	return 0;
}
 

随便输入一组数据看卡运行结果
在这里插入图片描述
再附上一张证明他是平衡树的图,嘿嘿,懂的都懂,不懂的就。。。。嘿嘿嘿嘿
在这里插入图片描述

平衡二叉树分享结束,各位有问题请在下方留言(发现bug提醒博主哦^ _ ^)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值