广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言之平衡二叉树详解
  • 406
分享到

C语言之平衡二叉树详解

C语言二叉树C语言平衡二叉树 2023-05-17 17:05:25 406人浏览 八月长安
摘要

目录什么是平衡二叉树平衡二叉树的基本特点为什么会出现平衡二叉树二叉树四种不平衡的情况C语言实现平衡二叉树什么是平衡二叉树 平衡二叉树是具有平衡属性的有序二叉树,所谓的平衡即当前树的左

什么是平衡二叉树

平衡二叉树是具有平衡属性的有序二叉树,所谓的平衡即当前树的左右子树高度差的绝对值不超过1。因为平衡二叉树是由苏联数学家Adelson-Velskii和Landis提出,所以又称为AVL树。

平衡二叉树的基本特点

  • 是特殊的有序二叉树
  • 左右子树高度差的绝对值不超过1
  • 左右子树仍然是平衡二叉树

为什么会出现平衡二叉树

学习平衡二叉树之前必定已经学过有序二叉树,有序二叉树的结构特点就是将数据有序的排好,查找起来快,但是有序二叉树有一个缺点,就是当节点呈现的状态是一边倒,那查找数据的时候就没有发挥出二叉树折半查找的优势了,这个时候是线性的查找(类似于链表的查找)。平衡二叉树就是解决有序二叉树一边倒的问题。如果有序二叉树是平衡的,那么查找数据就很快。时间复杂度为O ( l o g n ) O(logn)O(logn)。这样就充分发挥了二叉树的优势。

二叉树四种不平衡的情况

当树的左右子树高度差的绝对值大于1的时候就需要进行旋转操作,将不平衡的树变成平衡的树。以下是会出现的四种不平衡的情况。

  • 左左不平衡
  • 右右不平衡
  • 左右不平衡
  • 右左不平衡

左左不平衡旋转成平衡状态:

右右不平衡旋转成平衡状态:

左右不平衡旋转成平衡状态:

右左不平衡旋转成平衡状态:

上面是图解这四种不平衡状态旋转成平衡状态的情况。

C语言实现平衡二叉树

平衡二叉树的结构体描述:

#define Ty int  //以整型数据为例
typedef struct node
{
	Ty data;             //数据
	int height;          //高度
	struct Node* LChild; //左子树
	struct Node* RChild; //右子树
}Node,AVLTree;

初始化函数:

AVLTree* creatAVLTree(Ty data)
{
    AVLTree* tree = (AVLTree*)malloc(sizeof(AVLTree));
    assert(tree);
    tree->data = data;
    tree->height = 0;
    tree->LChild = NULL;
    tree->RChild = NULL;
    return tree;
}

辅助宏函数:

//辅助函数
#define HEIGHT(x) ((x==NULL)?(-1):(x->height))
#define MAX(a,b)  ((a>b)?(a):(b))
//获取树的新高度
#define GET_NEW_HEIGHT(x) (MAX(HEIGHT(x->LChild),HEIGHT(x->RChild)) + 1)

使用宏函数的好处是运行过程中不需要进行函数压栈的操作,效率快一点。

前序遍历平衡二叉树:

//前序打印
void show_pre(AVLTree* root)
{
	if(root==NULL)
		return;
	printf("data:%d\theight:%d\n",root->data,root->height);
	show_pre(root->LChild);
	show_pre(root->RChild);
}

使用前序遍历能更好地看出节点的高度,更方便还原平衡二叉树。

四种不平衡状态旋转的算法实现:

算法核心思想:找到新根的位置,然后进行对应的调整,最后返回新根的地址,这样就实现了旋转的操作,因为旋转后节点的高度改变了,所以在返回之前先调整一下节点的高度。

例如:左左不平衡进行旋转操作

因为是左左不平衡,所以新根的位置是当前根的左子树,那就使用一个指针(newRoot)去接收当前根的左子树,然后使劲将当前根拉下来,让新根代替当前根的位置,那就必须将当前根的LChild指向newRoot的右子树(因为newRoot不一定是空的所以不能直接让curRoot→LChild指向空)。最后就是将newRoot→RChild指向curRoot这样就把位置调整好了。在返回newRoot之前把curRoot和newRoot的高度调整一下。保持树的准确性。

其他的不平衡情况也是类似的操作。

//出现不平衡的情况
//左左不平衡
Node *LL_Rotation(Node *curRoot)
{
	Node *newRoot = curRoot->LChild;
	curRoot->LChild = newRoot->RChild;
	newRoot->RChild = curRoot;

	curRoot->height = GET_NEW_HEIGHT(curRoot);
	newRoot->height = GET_NEW_HEIGHT(newRoot);
	return newRoot;
}

//右右不平衡
Node *RR_Rotation(Node *curRoot)
{
	Node *newRoot = curRoot->RChild;
	curRoot->RChild = newRoot->LChild;
	newRoot->LChild = curRoot;

	curRoot->height = GET_NEW_HEIGHT(curRoot);
	newRoot->height = GET_NEW_HEIGHT(newRoot);
	return newRoot;
}
//左右不平衡
Node *LR_Rotation(Node *curRoot)
{
	curRoot->LChild = RR_Rotation(curRoot->LChild);
	return LL_Rotation(curRoot);
}
//右左不平衡
Node *RL_Rotation(Node *curRoot)
{
	curRoot->RChild = LL_Rotation(curRoot->RChild);
	return RR_Rotation(curRoot);
}

平衡二叉树的插入操作:

插入操作需要考虑到四种情况:

  • 当前节点是空的
  • 要插入进来的数据比当前节点的数据小
  • 要插入进来的数据比当前节点的数据大
  • 要插入进来的数据和当前节点的数据一样大

情况一的解决方案:直接申请一个节点内存。

情况二的解决方案:递归往左边跑,然后跑到对应的位置就申请内存,插入完成后判断需不需要调整。

情况三的解决方案:递归往右边跑,然后跑到对应的位置就申请内存,插入完成后判断需不需要调整。

情况四的解决方案:因为我们做的是一棵没有重复数据的平衡二叉树所以遇到这种情况的时候不进行插入操作。当然如果做的是一棵可以有重复数据的平衡二叉树,那遇到这种情况的时候可以个人的想法放左边放右边都可以。

源代码:

//插入数据
Node *insertNode(Node *curRoot, Ty data)
{
	//插入分有四个大情况
	if (curRoot == NULL)			  //当前节点是空的
		curRoot = creatAVLTree(data); //如果是空就直接创建一个新的节点
	else if (data < curRoot->data)	  //要插入的数据比当前节点的数据小
	{
		//往左边跑
		curRoot->LChild = insertNode(curRoot->LChild, data);
		//插入完成之后,判断需不需要调整树
		if (HEIGHT(curRoot->LChild) - HEIGHT(curRoot->RChild) == 2)
			//因为插入的位置在左边,所以插入完成之后,左子树的高度大于等于右子树高度
			curRoot = data < curRoot->LChild->data ? LL_Rotation(curRoot) : LR_Rotation(curRoot);
	}
	else if (data > curRoot->data) //要插入的数据比当前节点的数据大
	{
		//往右边跑
		curRoot->RChild = insertNode(curRoot->RChild, data);
		if (HEIGHT(curRoot->RChild) - HEIGHT(curRoot->LChild) == 2)
			//因为插入的位置在右边,所以插入完成之后,右子树的高度大于等于左子树高度
			curRoot = data > curRoot->RChild->data ? RR_Rotation(curRoot) : RL_Rotation(curRoot);
	}
	else //要插入的数据和当前节点的数据一样大
		printf("无法插入数据\n");
	//获取新高度
	curRoot->height = GET_NEW_HEIGHT(curRoot);
	return curRoot; //插入完成之后返回当前节点的指针
}

平衡二叉树的删除操作:

删除操作也是要考虑四个大情况:

  • 当前节点是空的
  • 要删除的数据比当前数据小
  • 要删除的数据比当前数据大
  • 要删除的数据和当前数据一样大

情况一的解决方案:没有删除的必要了,结束掉函数

情况二的解决方案:往左边递归找到对应位置,然后进行删除操作

情况三的解决方案:往右边递归找到对应位置,然后进行删除操作

情况四的解决方案:针对这个情况又要分为两个小情况

  • 当前节点的左子树和右子树都存在
  • 当前节点的左右子树至多有一个存在

具体解决方案看代码和注释

源代码:

//查找节点
//找最大节点
Node *maxNode(Node *curRoot)
{
	if (curRoot == NULL)
		return NULL;
	//往右边找
	while (curRoot->RChild)
		curRoot = curRoot->RChild;
	return curRoot;
}

//找最小节点
Node *minNode(Node *curRoot)
{
	if (curRoot == NULL)
		return NULL;
	while (curRoot->LChild)
		curRoot = curRoot->LChild;
	return curRoot;
}

//删除数据
Node *deleteNode(Node *curRoot, Ty data)
{
	//删除数据有四个大情况
	if (curRoot == NULL)	  //当前节点是空的
		return NULL;		  //删除了个寂寞直接结束掉整个函数
	if (data < curRoot->data) //要删除的数据比当前节点的数据小
	{
		//往左边跑
		curRoot->LChild = deleteNode(curRoot->LChild, data);
		//获取新高度
		curRoot->height = GET_NEW_HEIGHT(curRoot);
		//判断需不需要调整
		if (HEIGHT(curRoot->RChild) - HEIGHT(curRoot->LChild) == 2)
			curRoot = HEIGHT(curRoot->RChild->LChild) > HEIGHT(curRoot->RChild->RChild) ? RL_Rotation(curRoot) : RR_Rotation(curRoot);
	}
	else if (data > curRoot->data) //要删除的数据比当前节点的数据大
	{
		//往右边跑
		curRoot->RChild = deleteNode(curRoot->RChild, data);
		curRoot->height = GET_NEW_HEIGHT(curRoot);
		if (HEIGHT(curRoot->LChild) - HEIGHT(curRoot->RChild) == 2)
			curRoot = HEIGHT(curRoot->LChild->RChild) > HEIGHT(curRoot->LChild->LChild) ? LR_Rotation(curRoot) : LL_Rotation(curRoot);
	}
	else
	{ //要删除的数据和当前节点的数据一样大
		//针对于curRoot这个节点做删除操作
		//主要有两个主要的情况
		if (curRoot->LChild && curRoot->RChild) // curRoot有左子树和右子树
		{
			//先判断左右子树的高度,将高度比较高的子树的节点拿到上面来
			if (HEIGHT(curRoot->LChild) > HEIGHT(curRoot->RChild))
			{ //左子树的高度比右子树的高度高
				//找到左子树的最大节点
				Node *max = maxNode(curRoot->LChild);
				//找到之后就将max的数据替换curRoot的数据
				curRoot->data = max->data;
				//赋值完成之后继续递归,然后释放掉max对应的节点,不能直接在这里释放,因为要调整树的高度
				curRoot->LChild = deleteNode(curRoot->LChild, max->data);
			}
			else
			{ //左子树的高度小于等于右子树的高度
				//找到右子树的最小节点
				Node *min = minNode(curRoot->RChild);
				curRoot->data = min->data;
				curRoot->RChild = deleteNode(curRoot->RChild, min->data);
			}
		}
		else //上一种情况的否定,即curRoot没有子树或者只有一边
		{
			//释放内存
			Node *temp = curRoot;
			// curRoot拿到存在的子树
			curRoot = curRoot->LChild ? curRoot->LChild : curRoot->RChild;
			free(temp);
		}
	}

	return curRoot; //删除完成之后就返回当前节点
}

主函数测试

int main()
{
	AVLTree *tree = NULL;
	for (int i = 1; i < 10; i++)
		tree = insertNode(tree, i);
	show_pre(tree); //前序打印树
	printf("----------------------------\n");
	//删除6这个节点
	tree = deleteNode(tree, 6);
	show_pre(tree);
	printf("程序结束\n");
	return 0;
}

运行结果:

删除前和删除后的平衡二叉树:

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

--结束END--

本文标题: C语言之平衡二叉树详解

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

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

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

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

下载Word文档
猜你喜欢
  • C语言之平衡二叉树详解
    目录什么是平衡二叉树平衡二叉树的基本特点为什么会出现平衡二叉树二叉树四种不平衡的情况C语言实现平衡二叉树什么是平衡二叉树 平衡二叉树是具有平衡属性的有序二叉树,所谓的平衡即当前树的左...
    99+
    2023-05-17
    C语言二叉树 C语言平衡二叉树
  • C语言平衡二叉树详解
    目录调整措施:一、单旋转二、双旋转AVL树的删除操作:删除分为以下几种情况:1.要删除的节点是当前根节点T。2、要删除的节点元素值小于当前根节点T值,在左子树中进行删除。3、要删除的...
    99+
    2022-11-12
  • C语言平衡二叉树真题练习
    目录一、题目描述二、解题思路自顶向下的递归(暴力解法)自底向上的递归(最优解法)题目难度:简单 LeetCode链接:平衡二叉树 一、题目描述 给定一个二叉树,判断它是否是高度平衡的...
    99+
    2022-11-13
  • C语言平衡二叉树问题怎么解决
    这篇文章主要介绍“C语言平衡二叉树问题怎么解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C语言平衡二叉树问题怎么解决”文章能帮助大家解决问题。一、题目描述给定一个二叉树,判断它是否是高度平衡的二...
    99+
    2023-06-30
  • C语言平衡二叉树的示例分析
    这篇文章给大家分享的是有关C语言平衡二叉树的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一...
    99+
    2023-06-25
  • C语言数据结构之二叉树详解
    目录1. 树概念及结构1.1树概念1.2树的表示2. 二叉树概念及结构2.1概念2.2数据结构中的二叉树2.3特殊的二叉树2.4二叉树的存储结构2.5二叉树的性质3. 二叉树顺序结构...
    99+
    2022-11-13
  • 详解如何用c++实现平衡二叉树
    目录一、概述二、平衡二叉树不平衡的情形三、调整措施3.1、单旋转3.2、双旋转四、AVL树的删除操作五、代码实现一、概述 平衡二叉树具有以下性质:它是一 棵空树或它的左右两个子树的高...
    99+
    2022-11-12
  • 详解Java数据结构之平衡二叉树
    目录什么是二叉搜索树平衡二叉搜索树平衡二叉搜索树建树程序计算每个节点的高度计算每个节点的平衡因子合并二叉树旋转调整函数整体代码什么是二叉搜索树 简单来说,就是方便搜索的二叉树,是一种...
    99+
    2022-11-13
  • Java源码解析之平衡二叉树
    目录一、平衡二叉树的定义二、平衡二叉树的实现原理三、最终结果一、平衡二叉树的定义 平衡二叉树是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1 。它是一种高度平衡的二...
    99+
    2022-11-12
  • C语言之二叉树的遍历
    目录0.写在前面1.前序遍历步骤详解代码实现2.中序遍历步骤详解代码实现3.后序遍历步骤详解代码实现0.写在前面 认识二叉树结构最简单的方式就是遍历二叉树。所谓遍历二叉树就是按照某种...
    99+
    2023-05-14
    C语言实现二叉树遍历 二叉树遍历
  • C++怎么实现平衡二叉树
    本篇内容介绍了“C++怎么实现平衡二叉树”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!平衡二叉树Given a binary tree, d...
    99+
    2023-06-20
  • C++实现LeetCode(110.平衡二叉树)
    [LeetCode] 110.Balanced Binary Tree 平衡二叉树 Given a binary tree, determine if it is height-ba...
    99+
    2022-11-12
  • Java数据结构之平衡二叉树的实现详解
    目录定义结点结构查找算法插入算法LL 型RR 型LR 型RL 型插入方法删除算法概述实例分析代码完整代码定义 动机:二叉查找树的操作实践复杂度由树高度决定,所以希望控制树高,左右子...
    99+
    2022-11-13
  • C语言二叉树的概念结构详解
    目录1、树的概念及结构(了解)1.1树的概念:1.2树的表示法:2、二叉树的概念及结构2.1二叉树的概念2.2特殊的二叉树2.2二叉树的性质2.3二叉树的顺序存储2.4二叉树的链式存...
    99+
    2022-11-13
    C语言二叉树 C语言二叉树的创建
  • 详细理解平衡二叉树AVL与Python实
    上一篇文章讨论的二叉搜索树,其时间复杂度最好的情况下是O(log(n)),但是最坏的情况是O(n),什么时候是O(n)呢? 像这样: 如果先插入10,再插入20,再插入30,再插入40就会成上边这个样子 这个就像是双向链表,我们期望它...
    99+
    2023-01-30
    二叉树 详细 Python
  • C++树之遍历二叉树实例详解
    在讲遍历之前,我们要先创建一个树: #include <iostream> using namespace std; typedef struct node; ty...
    99+
    2022-11-11
  • C语言数据结构之二叉链表创建二叉树
    目录一、思想(先序思想创建)二、创建二叉树(1)传一级参数方法(2)传二级参数方法一、思想(先序思想创建) 第一步先创建根节点,然后创建根节点左子树,开始递归创建左子树,直到递归创建...
    99+
    2022-11-13
  • C语言中二叉树的后序遍历详解
    目录一.二叉树的后序遍历.(递归)二.二叉树的后序遍历(迭代)总结首先我们从两个方面讲解二叉树的后序遍历(递归+迭代) 一.二叉树的后序遍历.(递归) 思想: 首先我们从二叉树的根节...
    99+
    2022-11-13
  • C语言实例实现二叉搜索树详解
    目录有些算法题里有了这个概念,因为不知道这是什么蒙圈了很久。 先序遍历: root——>left——>right 中序遍历...
    99+
    2022-11-13
  • C语言 链式二叉树结构详解原理
    目录前言二叉树节点声明二叉树的遍历构建二叉树1.前序遍历2.中序遍历3.后序遍历二叉树节点的个数二叉树叶子节点的个数二叉树第K层节点个数二叉树的高度/深度二叉树查找值为x的节点整体代...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作