【二叉树】的顺序存储(堆的实现)

news2024/11/25 2:57:42

📙作者简介: 清水加冰,目前大二在读,正在学习C/C++、Python、操作系统、数据库等。

📘相关专栏:C语言初阶、C语言进阶、C语言刷题训练营、数据结构刷题训练营、有感兴趣的可以看一看。

欢迎点赞 👍 收藏 ⭐留言 📝 如有错误还望各路大佬指正!

✨每一次努力都是一种收获,每一次坚持都是一种成长✨       

在这里插入图片描述

目录

 前言

1. 堆

  1.1 什么是堆

  1.2 堆的性质

2. 堆的实现

 2.1 堆的向下调整 

 2.2 堆的向上调整

 2.3 堆的创建

 2.3.1 定义堆

2.3.2 初始化和销毁

2.3.3 入堆

2.4 出堆

 2.5 堆顶元素、判空、堆的数据个数

总结


 前言

        我们常见的二叉树有顺序存储和链式存储,顺序存储就是使用数组来存储二叉树,除了顺序存储,堆存储也是一种常见的选择。堆存储是一种基于数组的存储方式,它使用数组来表示堆的结构。在堆存储中,我们可以使用简单的数学公式来计算节点在数组中的索引,从而实现高效的访问和操作。


1. 堆

  1.1 什么是堆

        堆是一种特殊的二叉树,使用堆存储的二叉树都是完全二叉树,堆又可分为大堆和小堆。

大堆:大堆的子节点不得大于父节点。

小堆:小堆的子节点不得小于父节点。

 可见文章:二叉树的存储结构

  1.2 堆的性质

         堆具有以下两个重要的性质:堆是一个完全二叉树,且堆中的每个节点的值都大于或等于(或小于或等于)其子节点的值,这被称为堆的堆序性质。

2. 堆的实现

 2.1 堆的向下调整 

         我们知道,堆是使用数组存储的,那任何一个数组都可以是堆吗?当然不是,对具有两个重要的性质,要想构成堆,就需要对数组的数据进行调整。调整后,逻辑上就是一颗完全二叉树。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

 如下图:

         在逻辑上是一个完全二叉树,根节点的值是30,根节点的左右子树都是小堆,为了使整颗二叉树形成堆,就需要对数据进行调整。(小堆的子节点不得小于它的父节点

 30比15大,就向下进行调整,15与30位置交换:

以此类推,进行调整:

 最终,被调整为一个小堆。(也可以调整为大堆)。

根据上述的逻辑,我们对向小调整的代码进行实现:

void HeapAdjustDown(HeapDatatype* data,int n,int parent)
{
	int child = parent * 2 + 1;
	while (child<n)			//这里不能child+1<n
	{
		if (child+1<n && data[child + 1] < data[child])//改大堆两个if的符号都要改
		{
			child++;
		}
		if(data[child]<data[parent])
		{
			swap(&data[parent], &data[child]);//后续需要多次进行调用交换函数,就封装成一个函数
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}

	}
	
}

        传入一个堆、节点个数和父节点,通过父节点与孩子节点进行比较,找到较小的孩子,然后进行交换。n可以用于判断结束。注意:这里存在比较坑的点,这里我们使用了child+1,就需要判断child+1是否会越界的问题。

那为什么不在while循环处判断?

        如果在while处判断就会产生新的bug,上述的例子无法展现,我们假设上述的二叉树,需要把30调整到60的位置,那30就会先和56进行交换,然后把child赋值给parent,child=child*2+1,赋值后,child就变成60这个节点,child+1就是空,那最后一次交换就不会执行。

 2.2 堆的向上调整

         堆可以向下调整,那也可以进行向上调整,向上调整和向下调整十分的相似,也是进行比较然后交换。向上调整也更为简单一些。流程如下:

         假设传入的子节点是80,80和父节点进行比较,80大进行交换,然后再和父节点比较,80仍然较大,再进行交换,直到形成一个堆结束。(此处为大堆),向下调整我们写的是调整小堆,这里我们向上调整也调整为小堆。代码如下:

void HeapAdjustUP(HeapDatatype* data, int child) {
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (data[child] < data[parent]) //回调函数qsort的compare函数
		{
			swap(&data[child], &data[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

        写代码时要注意:判断循环结束的条件,这里为什么不用parent>0进行判断?如果最后需要对整棵树的根节点进行交换,parent是可以等于0的,而child等于0时,即最终树的根节点交换完毕,如果再-1除2,还是0.所以拿child>0作为判断条件即可,如果没有交换到根节点,就已经构成了堆,那就break跳出循环即可。

 2.3 堆的创建

         注意上述堆的调整我们传入的参数,并不是创建的Heap(堆),而是以数组的形式传入,这样我们就可以直接调用这两个函数接口进行建堆。

for (int i = ; i < ; i++)
{
	AdjustUp(arr, i);
}

        只需这样就可以构建一个堆。这里我们就不进行细说,后续的堆排序会进行介绍。这里我们用老常规的方法,创建一个顺序表,然后进行建堆。

 2.3.1 定义堆

 顺序表的定义就非常常规了,和之前一样:

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

2.3.2 初始化和销毁

 初始化

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

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

 销毁

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

	free(php->data);
	php->data = NULL;
	php->capacity = php->size = 0;
}

 初始化和销毁都很常规,和顺序表一致。

2.3.3 入堆

         把数据入堆,先把数据入到顺序表中,然后进行调整。我们可以调用向上调整或者向下调整,只是传入的参数不同。

 代码如下:

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

		php->data = tmp;
		php->capacity = newcapacity;
	}
	php->data[php->size++] = data;
	HeapAdjustUP(php->data, php->size - 1);
}

使用循环调用入堆接口,就可以完成堆的创建。

2.4 出堆

         出堆,我们可以来思考一下,出堆是单纯的将数组最后一个数据删除掉吗?当然不是,如果只是单纯的删除最后的数据,那和顺序表又有什么区别,这样做是没有意义的。

        堆在数组中的第一个元素一定是这个数组中的最值(最大或最小值),所有我们出堆,删除的是数组中的第一个元素,也就是二叉树的根。那我们要怎么去删除呢?直接删除二叉树的根?

 如下图:

         如果直接删除二叉树的根节点,剩下的部分产生的结构就会发生变化,变化后的二叉树还是堆吗?所以说直接删除的方法是不可行的。那要如何删除呢?

        我们可以这样搞:可以先把数组的第一个元素和最后一个元素进行交换,然后删除最后一个元素,最后再进行调整。这样就既可以删除根节点,而又不打乱堆的结构。

 根据这个逻辑,代码实现如下:

void PopHeap(HP* php)
{
	assert(php);
	assert(php->size > 0);

	swap(&php->data[0], &php->data[php->size - 1]);
	php->size--;									
	
	HeapAdjustDown(php->data,php->size,0);
	
}

注意这里只能使用向下调整

 2.5 堆顶元素、判空、堆的数据个数

 剩余部分就非常简单了

//堆顶元素
HeapDatatype HeapTop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	return php->data[0];
}
//判空
bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}
//堆的数据个数
int HeapSize(HP* php) {
	assert(php);
	return php->size;
}

最后我们可以来测试一下,我的测试代码如下:

int main()
{
	HP hp;
	int arr[6] = { 70,65,100,32,50,60 };
	InitHeap(&hp);
	for (int i = 0; i < 6; i++)
	{
		PushHeap(&hp, arr[i]);
	}
	HeapPrint(&hp);
	printf("堆的数据个数:%d\n", HeapSize(&hp));
	while (!HeapEmpty(&hp))
	{
		printf("%d ", HeapTop(&hp));
		PopHeap(&hp);
	}
	DestoryHeap(&hp);
}

总结

        无论是学习数据结构还是应用数据结构,了解和理解堆存储都是非常有价值的。希望本篇博客能够为大家提供有价值的知识和见解,帮助大家更好地理解堆。最后,感谢阅读!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【学习笔记】各类基于决策单调性的dp优化

文章目录 对于决策单调性的一般解释关于决策单调性的证明四边形不等式一维dp区间dp一种二维dp一些满足四边形不等式的函数类 与图形相结合 决策单调性的常见优化手段二分队列二分栈分治类莫队做法 SMAWKWQS二分WQS多解情况满足四边形不等式的序列划分问题的答案凸性以及WQS二分…

老挝市场最全开发攻略

本文目录&#xff1a; 1.老挝国家概况 2.老挝节日与禁忌 3.老挝贸易现状与特点 4.老挝热销品类 5.老挝开发渠道 6.老挝注意事项 7.老挝国家冷知识 一、老挝国家概况 老挝人民民主共和国&#xff08;The Lao People’s Democratic Republic&#xff09;&#xff0c;简称…

基于python求两个数最大公约数函数gcd

一、gcd函数 在Python中&#xff0c;可以使用math模块中的gcd()函数来求两个数的最大公约数。首先需要导入math模块&#xff0c;然后使用gcd()函数来计算最大公约数。 二、示例 以下是使用Python求两个数最大公约数的示例代码&#xff1a; import math # 定义两个数 a 36 …

20230916后台面经整理

1.面对抢优惠券这样的高负载场景&#xff0c;你从架构、负载均衡等方面说一下你的设计&#xff1f; 答了参考Nginx进行负载均衡&#xff0c;然后在每台服务器怎么怎么弄&#xff08;架构每一层怎么设计&#xff09; 参考https://toutiao.io/posts/6z3uu2m/preview&#xff0c;h…

go 1.18新特性(泛性 模糊测试 WorkSpace)

文章目录 泛型interface的功能扩展泛型使用 模糊测试模糊测试的使用模糊测试的缺点 Go WorkSpace 泛型 interface的功能扩展 在1.18之前&#xff0c;官方对interface的定义是方法的集合&#xff0c;可以存储实现该方法的任意类型。1.18对interface的定义改为类型集合。接口类…

前端实现打字效果

前端实现打字效果 不带光标 只一次播放 HTML <!-- 需要在初始化的时候不显示文字 --> <div id"typing"></div>CSS #typing {position: relative;font-size: 24px;font-family: Arial, sans-serif;padding: 10px; }JS const text "要显…

CocosCreator3.8研究笔记(十八)CocosCreator UI组件(二)

前面的文章已经介绍了Canvas 组件、UITransform 组件、Widget 组件 。 想了解的朋友&#xff0c;请查看 CocosCreator3.8研究笔记&#xff08;十七&#xff09;CocosCreator UI组件&#xff08;一&#xff09;。 今天我们主要介绍CocosCreator 常用容器组件&#xff1a;Layout …

JavaScript-Ajax-axios-Xhr

JS的异步请求 主要有xhr xmlHttpRequest 以及axios 下面给出代码以及详细用法&#xff0c;都写在了注释里 直接拿去用即可 测试中默认的密码为123456 账号admin 其他一律返回登录失败 代码实例 <!DOCTYPE html> <html lang"en"> <head><…

预编译为什么能防止SQL注入?一看你就明白了。预编译原理详解

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 预编译防止SQL注入 1、SQL执行过程2、预编译原理3、…

爬虫框架Scrapy学习笔记-1

前言 在现代互联网时代&#xff0c;网页数据获取和处理已经成为了重要的技能之一。无论是为了获取信息、做市场研究&#xff0c;还是进行数据分析&#xff0c;掌握网页爬取和数据处理技术都是非常有用的。本文将介绍从网页加载到数据存储的完整过程&#xff0c;包括网络请求、…

银河麒麟操作系统安装人大金仓数据库--九五小庞

一、环境要求 硬件&#xff1a;内存512M以上&#xff0c;磁盘空间10G以上软件&#xff1a;主流Linux操作系统&#xff0c;本机使用kylin-v10安装包准备&#xff1a;官网下载数据库文件镜像以及授权文件 https://www.kingbase.com.cn/rjcxxz/index.htm 二、配置内核参数 vim /e…

Windows11 环境安装Gradle

Gradle和maven对比 maven 经典好用&#xff0c;约定大于配置的方式&#xff0c;使其上手简单&#xff0c;但灵活性稍差。 使用xml配置方式管理依赖&#xff0c;看起来稍丑。 在大型项目上&#xff0c;每次编译都要重新执行所有步骤会导致耗时很长。 Gradle 更加现代的构建工具…

高阶数据结构(2)-----红黑树(未完成)

一)红黑树的基本概念和基本性质: 1)红黑树就是一种高度平衡的二叉搜索树&#xff0c;但是在每一个节点上面都增加了一个存储位来表示结点的颜色&#xff0c;可以是红色或者是黑色&#xff0c;通过对任何一条从根节点到叶子节点上面的路径各个节点着色方式的限制&#xff0c;红黑…

【LeetCode热题100】--283.移动零

283.移动零 使用双指针&#xff1a; class Solution {public void moveZeroes(int[] nums) {if(nums null){return ;}int j 0;for(int i 0;i<nums.length;i){//当前元素不为0时&#xff0c;就把其交换到左边&#xff0c;等于0的交换到右边if(nums[i] ! 0){int tmp nums…

在华为云服务器上安装单机版Redis

https://redis.io/是官网地址。 点击右上角的Download。 可以进入https://redis.io/download/——Redis官网下载最新版的网址。 然后在https://redis.io/download/页面往下拉&#xff0c;点击下图超链接这里。 进入https://download.redis.io/releases/下载自己需要的安装包…

4795-2023 船用舱底水处理装置 学习记录

声明 本文是学习GB-T 4795-2023 船用舱底水处理装置. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件规定了船用舱底水处理装置(以下简称处理装置)中舱底水分离器(以下简称分离器)和舱底 水报警装置(以下简称报警装置)的要求、试验方法…

mysql 注入之权限

SQL注入之高权限注入 在数据库中区分有数据库系统用户与数据库普通用户,二者的划分主要体现在对一些高级函数与资源表的访问权限上。直白一些就是高权限系统用户拥有整个数据库的操作权限,而普通用户只拥有部分已配置的权限。 网站在创建的时候会调用数据库链接,会区分系统用…

面相面试知识--Lottery项目

面相面试知识–Lottery项目 1.设计模式 为什么需要设计模式&#xff1f; &#xff08;设计模式是什么&#xff1f;优点有哪些&#xff1f;&#xff09; 设计模式是一套经过验证的有效的软件开发指导思想/解决方案&#xff1b;提高代码的可重用性和可维护性&#xff1b;提高团…

一文详解二叉搜索树

数据结构-二叉查找树 前言 **摘自百度百科&#xff1a;**二叉查找树&#xff08;Binary Search Tree&#xff09;&#xff0c;&#xff08;又&#xff1a;二叉搜索树&#xff0c;二叉排序树&#xff09;它或者是一棵空树&#xff0c;或者是具有下列性质的二叉树&#xff1a; 若…

SpringBoot运维实用篇

SpringBoot运维实用篇 ​ 基础篇发布以后&#xff0c;看到了很多小伙伴在网上的留言&#xff0c;也帮助超过100位小伙伴解决了一些遇到的问题&#xff0c;并且已经发现了部分问题具有典型性&#xff0c;预计将有些问题在后面篇章的合适位置添加到本套课程中&#xff0c;作为解…