创建一棵平衡二叉树(C语言)

先来了解一些基本概念

二叉树树高

叶节点的树高都为1,如果一个节点的左右节点的高度不同,则取两者中较大的节点的高度加1为该节点的高度,别忘了加一
树高
这里我们采用递归求节点的高度

int GetHeight(AVLTree AT)
{
	int HL, HR, MaxH;
	if (AT)//有这个节点,返回左右子树中较大的那个值再加一
	{
		HL = GetHeight(AT->Left);
		HR = GetHeight(AT->Right);
		MaxH = HL > HR ? HL : HR;
		return (MaxH + 1);
	}
	else//就是没有这个节点,返回0
		return 0;
}

平衡因子

平衡因子是判断平衡二叉树是否平衡的条件,每个节点的平衡因子都不同,(节点的平衡因子=该节点的左子树第一个节点的高度—该节点的右子树的第一个节点的高度)若无左(右)子树,则左(右)子树的高度为0。平衡二叉树的平衡因子的绝对值<=1,如果某个节点平衡因子的绝对值不满足这个条件,插入的这个节点就为问题节点,这是我们就需要进行调整,调整方法有左单旋,右单旋,左右双旋,右左双旋。
平衡因子

调整方法

这个是二叉树节点的结构体定义

typedef int ElementType;
typedef struct AVLNode* AVLTree;
struct AVLNode//二叉树节点结构体
{
	ElementType Data;//节点数据
	AVLTree Left;
	AVLTree Right;
	int Height;//节点高
};

每当插入一个数据节点后,都要判断二叉树是否平衡,检测到二叉树不平衡,就要通过左单旋,右单旋来调整二叉树使其达到平衡,二叉树的左单旋和右单旋混合使用,可以组成左右双旋和右左双旋

左单旋

如图,新插入C节点后A的平衡因子由1变成2,需要进行调整变成平衡二叉树
左单旋
下面这种是一般情况左单旋
这个是实现的代码

AVLTree SingleLeftRotation(AVLTree A)
{
	AVLTree B = A->Left;
	A->Left = B->Right;
	B->Right = A;
	//只需要更新A,B的节点的高度
	A->Height = MAX(GetHeight(A->Left), GetHeight(A->Right)) + 1;//更新A节点的树高
	B->Height = MAX(GetHeight(B->Left), A->Height) + 1;//更新B节点的树高
	return B;
}

右单旋

右单旋和左单旋就是对称的情况
右单旋
右单旋
下面是实现的代码

AVLTree SingleRightRotation(AVLTree A)
{
	AVLTree B = A->Right;
	A->Right = B->Left;
	B->Left = A;
	//只需要更新A,B的节点的高度
	A->Height = MAX(GetHeight(A->Left), GetHeight(A->Right)) + 1;//更新A节点的树高
	B->Height = MAX(GetHeight(B->Right), A->Height) + 1;//更新B节点的树高
	return B;
}

左右双旋

左右双旋就是先进行右单旋,再进行左单旋,下图是一种简单的情况
在这里插入图片描述
下图是左右双旋的一般情况
左右双旋
下面是实现的代码,通过调用左单旋和右单旋的函数实现

AVLTree DoubleLeftRightRotation(AVLTree A)
{
	A->Left = SingleRightRotation(A->Left);//先进行右单旋
	A = SingleLeftRotation(A);//后进行左单旋
}

右左双旋

右左双旋
右左双旋
下面是实现的代码,先调用左单旋函数,后调用右单旋函数

AVLTree DoubleRightLeftRotaion(AVLTree A)
{
	A->Right = SingleLeftRotation(A->Right);//先进行左单旋
	A = SingleRightRotation(A);//后进行右单旋
}

整体代码实现

本人的设计思路是用户每输入一个数据,都使用递归,当达到最里面一层嵌套时找到节点插入的位置,然后插入节点。随后嵌套逐渐退出,所操作的节点逐渐向根节点靠近,对于每一个所操作的节点都要判断其平衡因子是否符合条件,若不符合则根据不同情况进行调整,每次嵌套结束之前都要更新当前节点的高度,递归结束后二叉树达到平衡,返回二叉树根节点指针。如果用户输入的数据为0,则,输入结束,中序遍历输出二叉树。

//这是主函数
int main()
{
	int Data;
	AVLTree AT = NULL;
	while (1)
	{
		scanf_s("%d", &Data);
		if (!Data)如果输入为0就跳出循环
			break;
		AT = VALTreeInsert(AT, Data);//把数据插入二叉树中并做调整
	}
	printf("中序遍历输出结果是");
	InorderTraversal(AT);//中序遍历输出二叉树
	return 0;
}

接下来是二叉树的插入操作

AVLTree VALTreeInsert(AVLTree AT, ElementType Data)
{
	if (AT == NULL)//如果这个节点为空,就在这里建立一个节点,相当于把数据插入到二叉树上,接下来要做的就是判断和调整
	{
		AT = (AVLTree)malloc(sizeof(struct AVLNode));
		AT->Data = Data;
		AT->Left = AT->Right = NULL;//把指针置空
		AT->Height = 1;//记得置1
	}
	else if (Data < AT->Data)//如果要插入的数据的值小于当前节点的值,就插入到该节点的左子树
	{//递归遍历二叉树然后把数据插入到二叉树里面,此时我们已经跑到二叉树底层了,然后再从底层一点一点的往上返回判断平衡因子,发现不平衡问题就要调整
		AT->Left = VALTreeInsert(AT->Left, Data);//接着往左子树递归遍历下去
		if ((GetHeight(AT->Left) - GetHeight(AT->Right)) == 2)//因为是往该节点的左子树中插入数据,如果该节点变成了发现问题的节点,则该节点的平衡因子只能是2,不可能是-2
		{
			if (Data < AT->Left->Data)//如果要插入的数据小于该节点的左子树的第一个节点的数据,则表示该数据插入到了该节点的左子树的左边去了,我们要做的就是左单旋
				AT = SingleLeftRotation(AT);//左单旋
			else//否则就是插入到该节点的左子树的第一个节点的右边去了,我们要做的是左-右双旋
				AT = DoubleLeftRightRotation(AT);//左右双旋
		}
	}
	else if (Data > AT->Data)//如果要插入的数据的值大于当前节点的值,就插入到该节点的右子树
	{
		AT->Right = VALTreeInsert(AT->Right, Data);
		if ((GetHeight(AT->Left) - GetHeight(AT->Right)) == -2)//因为是往该节点的右子树中插入数据,如果这个节点变成了发现问题的节点,则该节点的平衡因子只能是-2,不可能是-2
		{
			if (Data > AT->Right->Data)//如果要插入的数据大于该节点的右子树的第一个节点的数据,则表示该数据插入到的该节点的右子树的右边去了,我们要做的是右单旋
				AT = SingleRightRotation(AT);//右单旋
			else//否则该数据就是插入到了该节点的右子树第一个节点的左边去了,我们要做的是右-左双旋
				AT = DoubleRightLeftRotaion(AT);//右左双旋
		}
	}
	else//如果要插入的数据的值等于当前节点的值,则输出该值已存在,不进行插入操作
		printf("该值已存在\n");
	AT->Height = MAX(GetHeight(AT->Left), GetHeight(AT->Right)) + 1;//插入节点,调整二叉树之后都要更新树的高度,别忘了加1
	return AT;
}

得,下面是完整代码

当然,递归可能会造成较大的空间复杂度,从而无法处理太大的数据量,读者理解了算法之后可以尝试使用非递归实现平衡二叉树

#include<stdio.h>
//
typedef int ElementType;
typedef struct AVLNode* AVLTree;
struct AVLNode
{
	ElementType Data;
	AVLTree Left;
	AVLTree Right;
	int Height;
};
/
int GetHeight(AVLTree AT);//计算树的高度
AVLTree VALTreeInsert(AVLTree AT, ElementType Data);//二叉树的插入兼调整操作
int MAX(int HL, int HR);//取两者中较大的那个数的值
AVLTree SingleLeftRotation(AVLTree A);//左单旋
AVLTree SingleRightRotation(AVLTree A);//右单旋
AVLTree DoubleLeftRightRotation(AVLTree A);//左右双旋
AVLTree DoubleRightLeftRotaion(AVLTree A);//右左双旋
void InorderTraversal(AVLTree AT);//中序遍历
/
int main()
{
	int Data;
	AVLTree AT = NULL;
	while (1)
	{
		scanf_s("%d", &Data);
		if (!Data)如果输入为0就跳出循环
			break;
		AT = VALTreeInsert(AT, Data);//把数据插入二叉树中并做调整
	}
	printf("中序遍历输出结果是");
	InorderTraversal(AT);//中序遍历输出二叉树
	return 0;
}
int GetHeight(AVLTree AT)
{
	int HL, HR, MaxH;
	if (AT)//有这个节点,返回左右子树中较大的那个值再加一
	{
		HL = GetHeight(AT->Left);
		HR = GetHeight(AT->Right);
		MaxH = HL > HR ? HL : HR;
		return (MaxH + 1);
	}
	else//就是没有这个节点,返回0
		return 0;
}
AVLTree VALTreeInsert(AVLTree AT, ElementType Data)
{
	if (AT == NULL)//如果这个节点为空,就在这里建立一个节点,相当于把数据插入到二叉树上,接下来要做的就是判断和调整
	{
		AT = (AVLTree)malloc(sizeof(struct AVLNode));
		AT->Data = Data;
		AT->Left = AT->Right = NULL;//把指针置空
		AT->Height = 1;//记得置1
	}
	else if (Data < AT->Data)//如果要插入的数据的值小于当前节点的值,就插入到该节点的左子树
	{//递归遍历二叉树然后把数据插入到二叉树里面,此时我们已经跑到二叉树底层了,然后再从底层一点一点的往上返回判断平衡因子,发现不平衡问题就要调整
		AT->Left = VALTreeInsert(AT->Left, Data);//接着往左子树递归遍历下去
		if ((GetHeight(AT->Left) - GetHeight(AT->Right)) == 2)//因为是往该节点的左子树中插入数据,如果该节点变成了发现问题的节点,则该节点的平衡因子只能是2,不可能是-2
		{
			if (Data < AT->Left->Data)//如果要插入的数据小于该节点的左子树的第一个节点的数据,则表示该数据插入到了该节点的左子树的左边去了,我们要做的就是左单旋
				AT = SingleLeftRotation(AT);//左单旋
			else//否则就是插入到该节点的左子树的第一个节点的右边去了,我们要做的是左-右双旋
				AT = DoubleLeftRightRotation(AT);//左右双旋
		}
	}
	else if (Data > AT->Data)//如果要插入的数据的值大于当前节点的值,就插入到该节点的右子树
	{
		AT->Right = VALTreeInsert(AT->Right, Data);
		if ((GetHeight(AT->Left) - GetHeight(AT->Right)) == -2)//因为是往该节点的右子树中插入数据,如果这个节点变成了发现问题的节点,则该节点的平衡因子只能是-2,不可能是-2
		{
			if (Data > AT->Right->Data)//如果要插入的数据大于该节点的右子树的第一个节点的数据,则表示该数据插入到的该节点的右子树的右边去了,我们要做的是右单旋
				AT = SingleRightRotation(AT);//右单旋
			else//否则该数据就是插入到了该节点的右子树第一个节点的左边去了,我们要做的是右-左双旋
				AT = DoubleRightLeftRotaion(AT);//右左双旋
		}
	}
	else//如果要插入的数据的值等于当前节点的值,则输出该值已存在,不进行插入操作
		printf("该值已存在\n");
	AT->Height = MAX(GetHeight(AT->Left), GetHeight(AT->Right)) + 1;//插入节点,调整二叉树之后都要更新树的高度,别忘了加1
	return AT;
}
int MAX(int HL, int HR)
{
	return HL > HR ? HL : HR;
}
AVLTree SingleLeftRotation(AVLTree A)
{
	AVLTree B = A->Left;
	A->Left = B->Right;
	B->Right = A;
	//只需要更新A,B的节点的高度
	A->Height = MAX(GetHeight(A->Left), GetHeight(A->Right)) + 1;//更新A节点的树高
	B->Height = MAX(GetHeight(B->Left), A->Height) + 1;//更新B节点的树高
	return B;
}
AVLTree SingleRightRotation(AVLTree A)
{
	AVLTree B = A->Right;
	A->Right = B->Left;
	B->Left = A;
	//只需要更新A,B的节点的高度
	A->Height = MAX(GetHeight(A->Left), GetHeight(A->Right)) + 1;//更新A节点的树高
	B->Height = MAX(GetHeight(B->Right), A->Height) + 1;//更新B节点的树高
	return B;
}
AVLTree DoubleLeftRightRotation(AVLTree A)
{
	A->Left = SingleRightRotation(A->Left);//先进行右单旋
	A = SingleLeftRotation(A);//后进行左单旋
}
AVLTree DoubleRightLeftRotaion(AVLTree A)
{
	A->Right = SingleLeftRotation(A->Right);//先进行左单旋
	A = SingleRightRotation(A);//后进行右单旋
}
void InorderTraversal(AVLTree AT)
{
	if (AT != NULL)
	{
		InorderTraversal(AT->Left);//输出左节点
		printf("%d ", AT->Data);//输出节点的值
		InorderTraversal(AT->Right);//输出右节点
	}
}
  • 19
    点赞
  • 86
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值