数据结构——顺序二叉树——堆

1.树的相关概念

        在介绍二叉树之前,我们首先要明确树是什么。

        树用我们的通常认识来判断应该是一种植物,从根向上生长,分出许多的树枝并长出叶子。对于数据结构中的树而言,其结构也正是从树的特征中剥离出来的。树结构是一种非线性数据结构,具有一个根结点,这一个根节点连接着其他若干个结点,这些结点也同样可以连接这其他若干个结点,如此形成的数据结构我们就称为树。

        子树即树的子集,我们可以认为根节点延伸出的若干个节点实际上是若干棵子树。注意,在树形结构的定义下子树之间不允许交集的存在,即树中不允许存在环。

        一个结点或树具有一些性质,一个结点的指的是其所含有的子树的个数,而一个树的度指的是这棵树的结点中最大的度;一个结点的层次指的是其对于根节点而言所处的层数,而一个树的高度/深度指的是这棵树的结点中最大的层次。

        按照结点的度和层次特点,可以分为:根节点——第一层;叶子结点——度为0;分支结点——度不为0;根据我们生活中的伦理关系延伸,一个结点的前驱结点称为父结点,后继结点称为子节点,同一个父结点的结点称为兄弟节点

2.二叉树相关概念与存储结构

2.1 二叉树概念

        二叉树是一种特殊的树。满足度为2的树就是二叉树,所以对于任意一个二叉树的节点我们都可以将其视为是一个根节点连接着左右两个子树的结构。

        

        满二叉树是一种特殊的二叉树,其每一层结点的个数都是满的。因此对于一个n层的满二叉树,其结点个数为2^k-1个。

        完全二叉树也是一种特殊的二叉树,其与和满二叉树相似,结点按层依次排序,到所有结点连接前不允许出现空位。

         值得注意的是:二叉树的度为0的结点个数一定比度为2的结点个数多一个

2.2二叉树的存储结构

2.2.1 顺序结构

        顺序结构采取数组来存储二叉树。数组根据二叉树按层的顺序将每个结点的值存进数组的对应位置。对于一棵完全二叉树,其父子结点之间在下标上存在固定的递推关系式,对于一个根节点位于下标为0位置的树而言:左孩子下标=父结点下标*2+1右孩子下标=父结点下标*2+2父结点下标=孩子结点下标/2。根据这个规律,我们便可以在数组中存下所有的二叉树结点并可以找到结点相关联的其它结点。

        可见,顺序二叉树按位置以此存储,这就意味着如果不是完全二叉树,那么在数组中就会出现空位。因此顺序二叉树一般只会用于完全二叉树来避免空间浪费。

2.2.2 链式结构

        链式二叉树使用链表来表示,链就作为二叉树的逻辑关系指示。链式二叉树结点存储着自己的数据和两个指针,指针分别指向左孩子和右孩子,由此组成最后的完整的二叉树。链式二叉树可以存储任意形式的二叉树,我们一般采用二叉链。

2.堆

2.1 堆的概念

        对于一个集合k,将其所有元素按照顺序二叉树的方式存入一个一维数组,对任意元素k_{i},若满足k_{i}\leq k_{2*i+1}k_{i} \leq k_{2*i+2}则为小堆;若满足k_{i} \geq k_{2*i+1}k_{i} \geq k_{2*i+2}则为大堆。

        通俗解释来说:堆是一个完全二叉树,当所有的父结点都小于等于自己的子结点时,构成了小堆;当所有的父结点都大于等于自己的子结点时,构成了大堆。

 2.2 堆的工程

2.2.1 堆的定义

        因为我们已经明确了,堆是使用数组进行存储,所以堆的实现方式应该和顺序表相同。

typedef int HPDataType;

typedef struct Heap
{
	HPDataType* data;
	int size;
	int capacity;
}HP;

 2.2.2 堆的函数接口

 2.2.2.1 堆的初始化与销毁

        堆的初始化与销毁和顺序表相同,不再过多叙述。

void HeapInit(HP* php)
{
	assert(php);

	php->data = NULL;
	php->capacity = php->size = 0;
}

void HeapDestroy(HP* php)
{
	assert(php);

	free(php->data);
	php->data = NULL;
	php->capacity = php->size = 0;
}
2.2.2.2 堆的大小、判空、取堆顶元素

        这些都是很基础的操作,与栈和队列相似,也不再详细说道。

int HeapSize(HP* php)
{
	assert(php);

	return php->size;
}

bool HeapEmpty(HP* php)
{
	assert(php);

	return php->size == 0;
}

HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	return php->data[php->size - 1];
}

2.2.3 堆的数据插入——向上调整

        对于一个堆,当我们插入一个数据的时候,这个数据会被插入到数组的尾部,也即二叉树的下一个结点处。但是我们的堆可不是那么随便的结构,小堆大堆数据之间有着自己特定的结构,这样直接在后面插入数据有可能会破坏其原有的结构,所以我们需要考虑如何再插入数据后保持其堆的特性。为此我们引入了向上调整算法

        向上调整算法目的是将新入堆的元素位置与原堆中的元素位置进行调整,使得再次构成一个堆。

        对于小堆而言,向上调整就是将子结点与其父亲相比,如果孩子小于父亲,则将二者位置调换,循环往复直到满足堆的条件为止。对于大堆而言,向上调整就是将子结点与其父亲相比,如果孩子大于父亲,则将二者位置调换,循环往复直到满足堆的条件为止。在调整过程中,每次交换后根据父子结点下标关系可以找到新的父结点与子结点。小堆和大堆的向上调整算法只在if语句中判断条件不同。

void AdjustUpMin(int* arr, int size)
{
	int child = size - 1;
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (arr[child] < arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void AdjustUpMax(int* arr, int size)
{
	int child = size - 1;
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (arr[child] > arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

        当有了向上调整算法后,我们就可以进行数据插入了。

void HeapPush(HP* php, HPDataType x)
{
	assert(php);

	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = realloc(php->data, sizeof(int) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		else
		{
			php->data = tmp;
		}
		php->capacity = newcapacity;
	}

	php->data[php->size] = x;
	php->size++;
	AdjustUp(php);
}

 2.2.4 堆的数据删除——向下调整

        对于堆来说,堆顶的元素是具有特殊意义的。小堆的堆顶是最小的,大队的堆顶是最大的。所以在对堆进行数据删除时,我们删除数组最后的元素意义不大,我们考虑的是取出堆顶元素后,针对元素堆顶进行删除。同理堆删除数据也会导致堆的结构被破坏,所以需要我们需要想办法恢复其堆的特性。这就需要我们设计向下调整算法

        向下调整算法的目的是将堆顶的元素与原堆中的元素交换位置,使得可以继续满足原来小堆或大堆的特征。

        对于小堆而言,向下调整是将其与子结点相比较,因为父结点需要小于子结点,所以我们需要选取左右孩子结点中较小的一个。为此我们可以假设左孩子是较小者,如果右孩子比左孩子小说明假设错误,让child+1即可。之后判断父结点与子结点的大小关系,如果满足小堆的要求就结束,否则将二者调换位置,然后找到新的父结点和子结点下标。如此循环,即可调整成为小堆。而对于大堆,和小堆同理,只是判断标准变成了父结点需要大于子节点,若父结点小于子节点则需要调整位置。

void AdjustDownMin(int* arr, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && arr[child] > arr[child + 1])
		{
			child++;
		}
		if (arr[child] < arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}

	}
}
void AdjustDownMax(int* arr, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && arr[child] < arr[child + 1])
		{
			child++;
		}
		if (arr[child] > arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

        于是乎,我们就可以写出数据删除的函数,这里是将堆顶元素删除,然后用最后一个元素代替堆顶元素,进行向下调整。

void HeapPop(HP* php)
{
	assert(php);

	Swap(&php->data[0], &php->data[php->size - 1]);
	php->size--;
	AdjustDown(php);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/321051.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

8 - MySQL数据读写分离|MySQL多实例

MySQL数据读写分离&#xff5c;MySQL多实例 MySQL数据读写分离数据读写分离如何实现数据的读写分离提供数据读写分离服务的软件&#xff08;中间件&#xff09;maxscale 软件提供的读写分离服务的工作过程配置数据读写分离结构 提供数据存储服务 MySQL多实例 MySQL数据读写分离…

[NAND Flash 6.4] NAND FLASH基本读操作及原理_NAND FLASH Read Operation源码实现

依公知及经验整理,原创保护,禁止转载。 专栏 《深入理解NAND Flash》 <<<< 返回总目录 <<<< ​全文 6000 字 内容摘要 NAND Flash 引脚功能 读操作步骤 NAND Flash中的特殊硬件结构 NAND Flash 读写时的数据流向 Read 操作时序 读时序操作过…

求斐波那契数列矩阵乘法的方法

斐波那契数列 先来简单介绍一下斐波那契数列&#xff1a; 斐波那契数列是指这样一个数列&#xff1a;1&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;5&#xff0c;8&#xff0c;13&#xff0c;21&#xff0c;34&#xff0c;55&#xff0c;89……这个数列从第3项开始 &…

webstorm最新版 激活 成功了

使用webstorm开发工具 很完美&#xff0c;第一次用webstorm IDE 开发工具就完美的激活了&#xff0c;你也不妨试试 链接地址&#xff1a;http://mano100.cn/thread-1942-1-1.html 激活后如下

DM数据库安装注意事项

数据库安装注意事项 一、安装前 一些参数需要在数据库创建实例前找用户确认。 参数名参数掩码参数值备注数据页大小PAGE_SIZE32数据文件使用的页大小(缺省使用8K&#xff0c;建议默认&#xff1a;32)&#xff0c;可以为 4K、8K、16K 或 32K 之一&#xff0c;选择的页大小越大…

Linux常用命令之cp、rm、touch、mv

cp: 复制文件或目录 -f 覆盖目标同名文件或目录时不进行提醒&#xff0c;而直接强制复制。-i 覆盖目标同名文件或目录时提醒用户确认。-p 复制时保持源文件的权限、属主及时间标记等属性不变&#xff08;默认权限属主是变化的&#xff09;。-r 复制目录时必须使用此选项&a…

Nacos注册中心-安装

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、认识Nacos二、安装Nacos1.直接方法nacos.io&#xff0c;点击view onGithub2.点击Releases3、点击Tags&#xff0c;可以看见所有版本&#xff0c;建议下载1.…

Baumer工业相机堡盟工业相机如何使用OpenCV实现相机图像的显示(C#)

Baumer工业相机堡盟工业相机如何使用OpenCV实现相机图像的显示&#xff08;C#&#xff09; Baumer工业相机Baumer工业相机的图像转换为OpenCV的Mat图像的技术背景在NEOAPI SDK里使用OpenCV实现相机图像的显示联合OpenCV实现相机图像的显示测试演示图 工业相机通过使用OpenCV实现…

史诗级长文--朴素贝叶斯

引言 朴素贝叶斯算法是有监督的学习算法&#xff0c;解决的是分类问题&#xff0c;如客户是否流失、是否值得投资、信用等级评定等多分类问题。该算法的优点在于简单易懂、学习效率高、在某些领域的分类问题中能够与决策树、神经网络相媲美。但由于该算法以自变量之间的独立&am…

java: 5-6 break

文章目录 1. break1.1 介绍1.2 语法和流程图1.3 入门练习1.4 细节说明1.5 练习 【老韩视频p137-】 1. break 看个需求&#xff1a;随机生成 1-100 的一个数&#xff0c;直到生成了 97 这个数&#xff0c;看看你一共用了几次? 【思路分析:循环&#xff0c;但是循环的次数不知道…

VQE音频处理流程

VQE 上行VQE&#xff0c;主要针对MIC采集部分的音频增强 下行VQE&#xff0c;主要针对SPK播放部分的音频增强 附关键词解释 RES RES 模块为重采样&#xff08;Resampler&#xff09;模块。当AI上行或AO下行通路中开启VQE 各功能 模块时&#xff0c;在处理前后各存在一次重采样…

实战之-Redis代替session实现用户登录

一、设计key的结构 首先我们要思考一下利用redis来存储数据&#xff0c;那么到底使用哪种结构呢&#xff1f;由于存入的数据比较简单&#xff0c;我们可以考虑使用String&#xff0c;或者是使用哈希&#xff0c;如下图&#xff0c;如果使用String&#xff0c;注意他的value&…

计算机网络技术-2022期末考试解析

【前言】 这是计算机网络技术这门课&#xff0c;感觉和计网还是有不一样的&#xff0c;但也有能做的&#xff0c;把能做的做了。 一、单项选择题&#xff08;每题2分&#xff0c;共20分&#xff09; 1. 用于测试两台计算机连通状况的命令是 。 ( ) A. cmd B. ping C. ipconf…

代码随想录day30 回溯算法最终章

51. N皇后 题目 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决方案。 每一种解法包含一个不同的 n 皇后问题 的棋子放置方案&#xff0c;该方案中 Q 和…

TIFF转JPG助手:轻松批量转换,优化图片管理

在数字时代&#xff0c;图片已成为我们生活和工作中不可或缺的一部分。为了更好地管理和使用这些图片&#xff0c;我们需要一个强大的工具来帮助我们转换和优化图片格式。TIFF转JPG助手正是这样一款理想的解决方案 首先&#xff0c;我们进入首助编辑高手主页面&#xff0c;会看…

嵌入式软件面试之程序在存储器中的分布

Hi, 大家好&#xff0c;今天阿目分享的是一个嵌入式软件面试的常见问题&#xff0c;内存分布或者说程序在内存中的布局&#xff0c;我们写的程序是按照怎么的准则放在内存中的&#xff1f; 一般有操作系统的嵌入式设备&#xff0c;都会有一个Bootloader, 它负责在上电后初始化…

70.网游逆向分析与插件开发-角色数据的获取-自动化助手UI显示角色数据

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;利用技能点属性分析角色数据基址-CSDN博客 码云地址&#xff08;ui显示角色数据 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;367aa71f60b…

HCIP之OSPF大实验

华子目录 实验拓扑及要求实验步骤合理划分网段配置IP地址两个area 0区域可以访问公网首先使用ospf使私网通在边界路由器上写静态缺省在边界路由器上做nat&#xff0c;实现公网访问在边界路由器上强制下放缺省 使用GRE连接两个area 0&#xff0c;解决不规则区域划分在边界路由器…

【Vue3】2-11 : 生命周期钩子函数及原理分析

本书目录&#xff1a;点击进入 一、组件生命周期概述 1.1 官方生命周期 1.2 钩子函数&#xff08;回调函数&#xff09; ▶ 生命周期可划分为三个部分(- >表示执行循序)&#xff1a; 二、实战&#xff1a;测试生命周期流程 &#xff1e; 代码 &#xff1e; 效果 一…

(十二)EEPROM的补充

文章目录 EEPROM补充篇读EEPROM补充内容写EEPROM补充内容单字节写入多字节拆成单字节写入现象 EEPROM补充篇 读EEPROM补充内容 对于上一篇博文在读EEPROM的时候&#xff0c;提到的DUMMY WRITE&#xff1a; 这里怎么理解呢&#xff1a; 大家看&#xff0c;写EEPROM的逻辑除了…