广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >如何使用C语言实现平衡二叉树数据结构算法
  • 121
分享到

如何使用C语言实现平衡二叉树数据结构算法

2024-04-02 19:04:59 121人浏览 独家记忆
摘要

目录前言一、平衡二叉树实现原理二、平衡二叉树实现算法三、全部代码前言 对于一个二叉排序树而言 它们的结构都是根据了二叉树的特性从最左子树开始在回到该结点上继续往右结点走

前言

对于一个二叉排序树而言

  • 它们的结构都是根据了二叉树的特性从最左子树开始在回到该结点上继续往右结点走
  • 通过该方式进行递归操作,并且该二叉排序树的结构也是从小到大依次显示
  • 那么我们假设a[10]={ 3,2,1,4,5,6,7,10,9,8 };我们需要查找改列表中的某一个结点的值

那么我们通过二叉排序树的展示,会展示成如图:

在这里插入图片描述 

可以发现,如果我们想通过二叉排序树这个深度为8的树来查找某个数

我们需要走到最后,这是最坏的打算

但是如果我们能将该树改变为平衡二叉树,如图所示:

在这里插入图片描述 

相比之下我们这个深度为4的平衡二叉树在通过查询值时是效率很平均且稳定的,

那么通过上面的问题我们该怎么将这个二叉排序树的变成平衡二叉树是我们现在的问题了。

一、平衡二叉树实现原理

对于平衡二叉树的特性而言,我们在上面也有举例到

  • 在二叉排序树中,按照它的从小到大的结构所排序下来的结点,
  • 当它的平衡因子的绝对值大于1的结点的根的子树,我们称为最小不平衡子树。

而我们该如何去计算该二叉排序树的平衡因子(平衡因子 = 左子树 - 右子树的值)呢?

我们可以结合上面那个最坏打算的二叉排序树的生成方式进行逐一分析。

如图:

在这里插入图片描述

可以看到当我们单步调试二叉排序树的时候到值1的时候值3的平衡因子为2>1

  • (左子树有值2、值1所以是2-右子树0所以等于2)

那么此时并不符合我们的平衡二叉树的规则

那么我们需要通过某种形式去调换,使其符合我们的平衡二叉树的规则

而图2就是由图1通过顺时针而得到出来的,在这里我们称为右旋

  • 此时我们所有的平衡因子<2那么又恢复了正常

之后我们的值4继续插入,因为值4>值3,所以根据二叉排序树的特性是值3的右子树

  • 此时我们查看图3发现所有的平衡因子<2,

那么也没出现什么问题之后继续插入下一个值5,因为值5>值4,所以值5会到值4的右子树上。

如图所示:

在这里插入图片描述

可以发现图4中当插入值5的时候,根节点、值3的平衡因子的绝对值=2

  • 所以是不符合平衡条件的所以此时我们通过逆时针的方式去旋转值3下面的所有结点
  • 在这里我们称为左旋,得到的结果即为图5所示了,且此时每个结点的平衡因子都<2,恢复平衡了

剩下就不多去演示了,整体是一个递归的过程

重要:在过程中反复的把不平衡的结点通过左、右旋的方式将其调整回平衡二叉树。

要想深刻了解平衡二叉树的实现原理请自行参考程杰著作的《大话数据结构》。

二、平衡二叉树实现算法

在类型上我们会通过二叉排序树的基础上添加一个bf,用于存储平衡因子,而定义了一个Status则是判断当前状态的代码。

代码如下:


typedef int Status;	

typedef  struct BiTnode	
{
	int data;	
	int bf; 
	struct BiTNode* lchild, * rchild;	
} BiTNode, * BiTree;

为了方便理解,我们前面的提到的左旋和右旋的方式,在C语言的代码中该如何实现呢?下面我们就来将代码展示出来。

代码如下:


void R_Rotate(BiTree* P)
{
	BiTree L;
	L = (*P)->lchild; 
	(*P)->lchild = L->rchild; 
	L->rchild = (*P);
	*P = L; 
}


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

我们可以看到左旋和右旋的代码很类似,所以我们通过了解了右旋的代码的含义自然就可以反推出左旋的含义了。

此函数的意思是:

  • 当某一个结点P处于一个不平衡状态的时候,且平衡因子为正数需要右旋(顺时针),并将它的左孩子结点定义为L。
  • 将L的右子树变成P的左子树,再将P改成L的右子树,最后将L替换P成功根结点。

如果文字还不了解我们可以将上述图1、图2的图片进行推导。

如图:

在这里插入图片描述 

那么左旋的操作就是一个逆时针的方向转换,代码也是类似这里就不再演示了。

  • 但是在这里我们还有另一种情况,就是当有两个或以上的平衡因子的绝对值>=2的时候,它们的平衡因子的值可能为负也可能为正,那么这种情况我们就要考虑到双旋操作了,
  • 我们假设以树根为顶点的左右子树都需要考虑,且它们是相对的,所以我们可以通过了解左平衡旋转处理也就继而反推出右平衡旋转处理了。

现在我们来看左平衡旋转处理的代码函数:


#define LH +1  
#define EH 0   
#define RH -1  


void RightBalance(BiTree* T)
{
	BiTree R, Rl;
	R = (*T)->rchild; 
	switch (R->bf)
	{ 
	case RH: 
		(*T)->bf = R->bf = EH;
		L_Rotate(T);
		break;
	case LH: 
		Rl = R->lchild; 
		switch (Rl->bf)
		{ 
		case RH: (*T)->bf = LH;
			R->bf = EH;
			break;
		case EH: (*T)->bf = R->bf = EH;
			break;
		case LH: (*T)->bf = EH;
			R->bf = RH;
			break;
		}
		Rl->bf = EH;
		R_Rotate(&(*T)->rchild); 
		L_Rotate(T); 
	}
}


void LeftBalance(BiTree* T)
{
	BiTree L, Lr;
	L = (*T)->lchild; 
	switch (L->bf)
	{ 
	case LH: 
		(*T)->bf = L->bf = EH;
		R_Rotate(T);
		break;
	case RH: 
		Lr = L->rchild; 
		switch (Lr->bf)
		{ 
		case LH: (*T)->bf = RH;
			L->bf = EH;
			break;
		case EH: (*T)->bf = L->bf = EH;
			break;
		case RH: (*T)->bf = EH;
			L->bf = LH;
			break;
		}
		Lr->bf = EH;
		L_Rotate(&(*T)->lchild); 
		R_Rotate(T); 
	}
}


如图:

在这里插入图片描述

首先通过代码可以看到,我们定义了三个常数变量,分别代码1、0、-1。

然后呢因为是左平衡旋转处理我们需要获取当前图片所示的P结点(也就是我之前假设的根节点的顶点)的左子树,

这里要注意重点:

  • 当我们这个函数可以执行的时候已经确认了当前子树是不平衡的状态;
  • 且结点N的值(即新插入结点) < 结点P的值(即上述图片根结点P)和结点P->左子树高于结点P->右子树需要调整;
  • 并且你还要记住此时进来的结点P才是不平衡状态,且通过该P结点->左子树的平衡因子来进行分支判断。
  1. 如果等于1了则执行一次右旋操作,即上图右旋推导的并且把它们的BF值(平衡因子)都改为0操作;
  2. 反之如果等于-1了则像上图一样进行双旋操作;
  3. 不过在那之前还要通过获取图2上L的右子树LR(贴合代码上的Lr);
  4. 然后再判断一下Lr的平衡因子的值并再次进行分支判断;
  5. 修改根节点P(即代码中的根节点T)根据Lr的BF值进行修改它们的BF值;
  6. 最后在将Lr的BF值赋为0,进行双旋操作这里先左旋后右旋;
  7. 那么右平衡旋转处理函数则取反。

三、全部代码


#include "stdio.h"    
#include "stdlib.h"   
#include "io.h"  
#include "math.h"  
#include "time.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 
typedef int Status;	

typedef  struct BiTNode	
{
	int data;	
	int bf; 
	struct BiTNode* lchild, * rchild;	
} BiTNode, * BiTree;


void R_Rotate(BiTree* P)
{
	BiTree L;
	L = (*P)->lchild; 
	(*P)->lchild = L->rchild; 
	L->rchild = (*P);
	*P = L; 
}



void L_Rotate(BiTree* P)
{
	BiTree R;
	R = (*P)->rchild; 
	(*P)->rchild = R->lchild; 
	R->lchild = (*P);
	*P = R; 
}
#define LH +1  
#define EH 0   
#define RH -1  


void LeftBalance(BiTree* T)
{
	BiTree L, Lr;
	L = (*T)->lchild; 
	switch (L->bf)
	{ 
	case LH: 
		(*T)->bf = L->bf = EH;
		R_Rotate(T);
		break;
	case RH: 
		Lr = L->rchild; 
		switch (Lr->bf)
		{ 
		case LH: (*T)->bf = RH;
			L->bf = EH;
			break;
		case EH: (*T)->bf = L->bf = EH;
			break;
		case RH: (*T)->bf = EH;
			L->bf = LH;
			break;
		}
		Lr->bf = EH;
		L_Rotate(&(*T)->lchild); 
		R_Rotate(T); 
	}
}


void RightBalance(BiTree* T)
{
	BiTree R, Rl;
	R = (*T)->rchild; 
	switch (R->bf)
	{ 
	case RH: 
		(*T)->bf = R->bf = EH;
		L_Rotate(T);
		break;
	case LH: 
		Rl = R->lchild; 
		switch (Rl->bf)
		{ 
		case RH: (*T)->bf = LH;
			R->bf = EH;
			break;
		case EH: (*T)->bf = R->bf = EH;
			break;
		case LH: (*T)->bf = EH;
			R->bf = RH;
			break;
		}
		Rl->bf = EH;
		R_Rotate(&(*T)->rchild); 
		L_Rotate(T); 
	}
}



Status InsertAVL(BiTree* T, int e, Status* taller)
{
	if (!*T)
	{ 
		*T = (BiTree)malloc(sizeof(BiTNode));
		(*T)->data = e; (*T)->lchild = (*T)->rchild = NULL; (*T)->bf = EH;
		*taller = TRUE;
	}
	else
	{
		if (e == (*T)->data)
		{ 
			*taller = FALSE; return FALSE;
		}
		if (e < (*T)->data)
		{ 
			if (!InsertAVL(&(*T)->lchild, e, taller)) 
				return FALSE;
			if (*taller) 
				switch ((*T)->bf) 
				{
				case LH: 
					LeftBalance(T);	*taller = FALSE; break;
				case EH: 
					(*T)->bf = LH; *taller = TRUE; break;
				case RH: 
					(*T)->bf = EH; *taller = FALSE; break;
				}
		}
		else
		{ 
			if (!InsertAVL(&(*T)->rchild, e, taller)) 
				return FALSE;
			if (*taller) 
				switch ((*T)->bf) 
				{
				case LH: 
					(*T)->bf = EH; *taller = FALSE;	break;
				case EH: 
					(*T)->bf = RH; *taller = TRUE; break;
				case RH: 
					RightBalance(T); *taller = FALSE; break;
				}
		}
	}
	return TRUE;
}
int main(void)
{
	int i;
	int a[10] = { 3,2,1,4,5,6,7,10,9,8 };
	BiTree T = NULL;
	Status taller;
	for (i = 0; i < 10; i++)
	{
		InsertAVL(&T, a[i], &taller);
	}
	printf("本样例建议断点跟踪查看平衡二叉树结构");
	return 0;
}

到此这篇关于如何使用C语言实现平衡二叉树 的文章就介绍到这了,更多相关C语言实现平衡二叉树内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 如何使用C语言实现平衡二叉树数据结构算法

本文链接: https://www.lsjlt.com/news/133054.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何使用C语言实现平衡二叉树数据结构算法
    目录前言一、平衡二叉树实现原理二、平衡二叉树实现算法三、全部代码前言 对于一个二叉排序树而言 它们的结构都是根据了二叉树的特性从最左子树开始在回到该结点上继续往右结点走 ...
    99+
    2022-11-12
  • 如何实现数据结构中的二叉树遍历算法
    今天就跟大家聊聊有关如何实现数据结构中的二叉树遍历算法,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。今天咱们来看一种新...
    99+
    2022-10-19
  • C语言数据结构二叉树之堆的实现和堆排序详解
    目录一、本章重点二、堆2.1堆的介绍2.2堆的接口实现三、堆排序一、本章重点 堆的介绍堆的接口实现堆排序 二、堆 2.1堆的介绍 一般来说,堆在物理结构上是连续的数组结构,在逻辑结构...
    99+
    2022-11-13
  • C语言如何实现通用数据结构中的通用椎栈
    今天就跟大家聊聊有关C语言如何实现通用数据结构中的通用椎栈,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。为大家分享了C语言实现通用数据结构之通用椎栈的具体代码,具体内容如下这是在通用...
    99+
    2023-06-21
  • C语言如何实现通用数据结构中的通用集合
    本篇文章为大家展示了C语言如何实现通用数据结构中的通用集合,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。这是在通用链表的基础上实现的集合,关于链表的实现参见:C语言实现通用数据结构之通用链表注意集合...
    99+
    2023-06-21
  • 存储和数据结构:如何使用 Go 和 Bash 实现高效的算法?
    存储和数据结构是计算机科学的基础,它们使得我们能够在计算机上处理和存储大量的数据。如何使用 Go 和 Bash 实现高效的算法呢?在本文中,我们将介绍一些使用 Go 和 Bash 实现常见算法的技巧。 Go 是一种现代化的编程语言,它具有高...
    99+
    2023-11-05
    bash 编程算法 存储
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作