【初阶数据结构】——堆的引入

目录

前言

一、二叉树的顺序结构及实现 

1.1二叉树的顺序结构

1.2堆的结构

二、堆的实现

2.1堆向上调整算法(堆的插入)

2.2堆向下调整算法(堆的删除)

2.3建堆的时间复杂度

2.4堆的创建

2.5堆的初始化和空间的销毁

2.6堆的插入

向上调整函数

交换函数

2.7堆的删除

向下调整函数

2.8堆的打印、取值、判空

三、完整代码


前言

上篇文章简单介绍树,讲解了最基本的二叉树,以及二叉树使用数组存储的顺序结构和使用链表存储的链式结构两种存储方式,今天就引入堆来实现二叉树。


一、二叉树的顺序结构及实现 

1.1二叉树的顺序结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而满二叉树和完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

1.2堆的结构

如果有一个关键码的集合K = { k0,k1 ,k1 ,…, },把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki<=K2*i+1 且Ki <=K2*i+2 (Ki >=K2*i+1 且Ki >=K2*i+2 ) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。 

堆的性质:

堆中某个节点的值总是不大于或不小于其父节点的值。

大堆:任何父亲大于等于孩子

小堆:任何父亲小于等于孩子
堆总是一棵完全二叉树。


二、堆的实现

2.1堆向上调整算法(堆的插入)

我们直到在数组中插入数据是在末尾插入,那么用堆来表示就是在有效数据下面做孩子或者父亲,依次插入数据和上面的父亲结点作比较,如果父亲大了就将父亲和孩子互换,一直换到度也就是第一个结点就形成小堆,反之则形成大堆。 


2.2堆向下调整算法(堆的删除)

我么以上面建的小堆为例如果我们删除第一个元素,按照惯例将后面的元素前移,就会形成新的堆但是新的堆不一定是我们的大堆或者小堆上面的情况纯属巧合,通过观察我们可以发现去掉第一个元素形成的左右子树依然是小堆,我们不妨将第一个元素和最后一个元素互换位置,这样最小的元素就在最后,指针前移就可以做到删除,然后第一个位置的两个子树都是小堆再从两个小堆的堆顶选出最小的交换,重复操作又可以是一个小堆了。


2.3建堆的时间复杂度

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个节点不影响最终结果): 

2.4堆的创建

typedef int HPDatatype;
typedef struct Heap
{HPDatatype* a;int size;int capacity;
}HP;

还是使用动态开辟空间,来实现;

2.5堆的初始化和空间的销毁

void HPInit(HP* php)
{assert(php);php->a = NULL;php->size = php->capacity = 0;
}

和顺序表一样,将指针置空和将容量,size置零 。

//销毁空间
void HPDes(HP* php)
{assert(php);free(php->a);php->size = php->capacity = 0;
}

使用free库函数释放动态开辟的空间,最后将容量和size置零。

2.6堆的插入

void HPPush(HP* php, HPDatatype x)
{assert(php);if (php->size == php->capacity){int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDatatype* tmp = (HPDatatype*)realloc(php->a, sizeof(HPDatatype)*newcapacity);if (tmp == NULL){perror("realloc failed");exit(-1);}php->a = tmp;php->capacity = newcapacity;}php->a[php->size] = x;php->size++;HPadjustUp(php->a, php->size-1);
}

和顺序表的形式差不多,进入函数判断空间是否足够,不够的话动态开辟新的空间,开辟不成功的话打印错误码并退出,成功的话插入有效数据,size++,然后使用向上调整函数调整成堆 。

向上调整函数

调整函数就是上面的堆的向上调整算法,运用父亲和孩子的下标关系调整。

void HPadjustUp(HPDatatype* a, int child)
{//找到父亲int parent = (child - 1) / 2;//根为0  当和根交换后child为0while (child > 0){//当child小时和父亲交换 建成小堆//当child大时和父亲交换 建成大堆if (a[parent] > a[child]){swap(&a[parent], &a[child]);child = parent;parent = (child - 1) / 2;}else{break;}}
}

交换函数

void swap(HPDatatype* x, HPDatatype* y)
{HPDatatype tmp = *x;*x = *y;*y = tmp;
}

取地址防止出函数创建的变量销毁,导致交换失败。

2.7堆的删除

void HPpop(HP* php)
{assert(php);assert(php->size);//先将头和尾交换  左右子树依然是完整的小/大堆,再从两个子堆中找出最大/小值;swap(&php->a[0], &php->a[php->size - 1]);php->size--;HPadjustDown(php->a, php->size, 0);
}

 进入函数先判断,如果size为0是会造成越界,再将头和尾交换,size减减,最后使用堆的向下调整算法调整成小堆。

向下调整函数

void HPadjustDown(HPDatatype* a, int n, int parent)
{//假设左孩子最小int child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child + 1] < a[child]){//假设失败 右孩子小++child;}else{if (a[child] < a[parent]){swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}
}

在这个函数中我们使用了一个假设法我们也不知道子堆的堆顶那个数据最小,但是两者是连续的先假设一个然后进行判断 ;这里一定要注意child的取值范围(child<n),防止越界。

2.8堆的打印、取值、判空

//堆的打印
void HPPrint(HP* php)
{for (int i = 0; i < php->size; i++){printf("%d ", php->a[i]);}printf("\n");
}

因为我们创建的是数组,遍历整个数组就可以。

HPDatatype HPtop(HP* php)
{assert(php);assert(php->size > 0);return php->a[0];
}

这里要注意size要大于0,当size为0是代表空。

bool HPempty(HP* php)
{assert(php);return php->size == 0;
}

当size为0时代表空,0==0返回true。


三、完整代码

#define _CRT_SECURE_NO_WARNINGS 67
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int HPDatatype;
typedef struct Heap
{HPDatatype* a;int size;int capacity;
}HP;
//初始化
void HPInit(HP* php)
{assert(php);php->a = NULL;php->size = php->capacity = 0;
}
//销毁空间
void HPDes(HP* php)
{assert(php);free(php->a);php->size = php->capacity = 0;
}
void swap(HPDatatype* x, HPDatatype* y)
{HPDatatype tmp = *x;*x = *y;*y = tmp;
}
//
void HPadjustUp(HPDatatype* a, int child)
{//找到父亲int parent = (child - 1) / 2;//根为0  当和根交换后child为0while (child > 0){//当child小时和父亲交换 建成小堆//当child大时和父亲交换 建成大堆if (a[parent] > a[child]){swap(&a[parent], &a[child]);child = parent;parent = (child - 1) / 2;}else{break;}}
}
void HPPush(HP* php, HPDatatype x)
{assert(php);if (php->size == php->capacity){int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDatatype* tmp = (HPDatatype*)realloc(php->a, sizeof(HPDatatype)*newcapacity);if (tmp == NULL){perror("realloc failed");exit(-1);}php->a = tmp;php->capacity = newcapacity;}php->a[php->size] = x;php->size++;HPadjustUp(php->a, php->size-1);
}
void HPPrint(HP* php)
{for (int i = 0; i < php->size; i++){printf("%d ", php->a[i]);}printf("\n");
}
void HPadjustDown(HPDatatype* a, int n, int parent)
{//假设左孩子最小int child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child + 1] < a[child]){//假设失败 右孩子小++child;}else{if (a[child] < a[parent]){swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}
}void HPpop(HP* php)
{assert(php);assert(php->size);//先将头和尾交换  左右子树依然是完整的小/大堆,再从两个子堆中找出最大/小值;swap(&php->a[0], &php->a[php->size - 1]);php->size--;HPadjustDown(php->a, php->size, 0);
}
HPDatatype HPtop(HP* php)
{assert(php);assert(php->size > 0);return php->a[0];
}
bool HPempty(HP* php)
{assert(php);return php->size == 0;
}
int main()
{HP hp;HPInit(&hp);int a[] = { 65,100,70,32,50,60 };for (int i = 0; i < sizeof(a) / sizeof(int); i++){HPPush(&hp, a[i]);}HPPrint(&hp);while (!HPempty(&hp)){printf("%d ", HPtop(&hp));HPpop(&hp);}HPDes(&hp);return 0;
}

最后的执行结果我们可以发现利用堆的删除可以实现排序,这就是我们下篇文章的内容利用堆实现排序,敬请期待!!! 

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

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

相关文章

二值贝叶斯滤波计算4d毫米波聚类目标动静属性

机器人学中有些问题是二值问题&#xff0c;对于这种二值问题的概率评估问题可以用二值贝叶斯滤波器binary Bayes filter来解决的。比如机器人前方有一个门&#xff0c;机器人想判断这个门是开是关。这个二值状态是固定的&#xff0c;并不会随着测量数据变量的改变而改变。就像门…

Python 序列排序

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 &#x1f447; &#x1f447; &#x1f447; 更多精彩机密、教程&#xff0c;尽在下方&#xff0c;赶紧点击了解吧~ python源码、视频教程、插件安装教程、资料我都准备好了&#xff0c;直接在文末名片自取就可 python中&…

【QandA C++】内存泄漏、进程地址空间、堆和栈、内存对齐、大小端和判断、虚拟内存等重点知识汇总

目录 内存泄漏 内存模型 、进程地址空间 堆和栈的区别 内存对齐 大端小端及判断 虚拟内存有什么作用 内存泄漏 概念: 是指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况, 内存泄漏并不是指内存在物理上的消失, 而是应用程序分配了某段内存后, 因为设计错误…

Docker 安装Redis(集群)

3主3从redis集群配置 1、新建6个docker容器 redis 实例 docker run -d --name redis-node-1 --net host --privilegedtrue -v /data/redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381 docker run -d --name redis-node-2 --ne…

2023 “华为杯” 中国研究生数学建模竞赛(E题)深度剖析|数学建模完整代码+建模过程全解全析

​ 问题一 血肿扩张风险相关因素探索建模 思路&#xff1a; 根据题目要求,首先需要判断每个患者是否发生了血肿扩张事件。根据定义,如果后续检查的血肿体积比首次检查增加≥6 mL或≥33%,则判断为发生了血肿扩张。 具体判断步骤: (1) 从表1中提取每个患者的入院首次影像检查…

python基础语法

目录 常量和表达式 变量和类型 1.整数int 2.小数float 3.字符串string 4.布尔类型bool 5.动态类型 注释 输入输出 输出 输入 运算符 算术运算符 关系运算符 逻辑运算符 赋值运算符 python和C、Java语法区别 创建一个python项目 常量和表达式 在python中&…

String的增删查【C++】

String的增删查【C】 前言string的增删查改构造与析构构造string(const char* str "")赋值构造string(const string& s1) 赋值重载析构函数增reservepush_backappendinsert 删erase 查迭代器流插入流提取流插入流提取 前言 从这里开始可以算是进入了STL的学习中…

CRM客户管理系统英文专业版

外资公司日常沟通的语言以英文为主&#xff0c;业务往来也是涉及到国内外&#xff0c;专业的英文版CRM系统很适合这样的业务团队&#xff0c;尤其CRM供应商是国际化企业&#xff0c;在海外也有分公司、办事处。 多语言 ZOHO支持多语种如英语、汉语、日语等28种语言&#xff0…

MySQL基础篇-函数

目录 1.字符串函数 2.数值函数 3.日期函数 4.流程函数 5.小结 在MySQL中&#xff0c;函数是一种数据库对象&#xff0c;用于执行特定的操作或计算&#xff0c;并返回结果。函数通常用于查询、数据处理和转换&#xff0c;以及在SQL语句中执行其他操作。MySQL提供了许多内置函…

linux驱动之input子系统简述

文章目录 一、什么是input子系统二、内核代码三、代码分析 一、什么是input子系统 Input驱动程序是linux输入设备的驱动程序&#xff0c;我们最常见的就按键&#xff0c;触摸&#xff0c;插拔耳机这些。其中事件设备驱动程序是目前通用的驱动程序&#xff0c;可支持键盘、鼠标…

C++ -- IO流

目录 C语言的输入与输出 CIO流 C标准IO流 C文件IO流 文件常见的打开方式如下 以二进制的形式操作文件 以文本的形式操作文件 读写结构体 stringstream的简单介绍 C语言的输入与输出 C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 scanf(): 从标准输…

零基础学空手道_3_空手道的站姿(上)

欢迎回来一起学习刚柔流空手道。 讲一些比较严肃的内容&#xff0c;就是礼仪和站姿。 空手道一开始不是要学习怎么打&#xff0c;而是要学习怎么去尊重别人和不打。所以礼仪很重要。 一切事情都是以礼仪开始&#xff0c;以礼仪结束。这叫以理始以理终。 空手道也是这样&#xf…

MySQL - DML数据增删改

功能介绍&#xff1a; DML&#xff08;Data Manipulation Language&#xff09;数据操作语言&#xff0c;用来对数据库中表的数据记录进 行增、删、改操作。 添加数据&#xff08;INSERT&#xff09; 基本语法&#xff1a;insert into 表名(字段列表) values (值列表); …

【问题解决】Android Studio 无法连接手机(荣耀90)无法识别手机usb

问题描述&#xff1a; 使用AS调试的时候遇到一个问题&#xff0c;由于是重装后的电脑&#xff0c;什么都没配置&#xff0c;但是两个旧手机都在安装SDK tools里的Google usb driver后直接连上AS&#xff0c;而我的新手机却死活连不上&#xff0c;查了一下午&#xff0c;啥方法都…

Redis原理(一):Redis数据结构(上)

文章目录 1、 Redis数据结构-动态字符串2、 Redis数据结构-intset3、 Redis数据结构-Dict4、 Redis数据结构-ZipList5、 Redis数据结构-ZipList的连锁更新问题6、 Redis数据结构-QuickList1、 Redis数据结构-动态字符串 我们都知道Redis中保存的Key是字符串,value往往是字符串…

MongoDB(二)基础操作 创建、删除,查询等

mongodb有一个特点&#xff0c;如果某个库&#xff0c;库下面没数据&#xff08;mongodb成集合&#xff09;&#xff0c;该库等于不存在的 mongodb只要创建一个库&#xff0c;在库下写入数据&#xff0c;该库才会生成 mongoshe [-hhost -pxxx] 创建数据库 use 数据库名 # 如果…

c语言常见字符函数、内存函数(详讲)

前言&#xff1a; 其实在c语言当中是没有字符串这一概念的&#xff0c;不像c里面有string类型用来存放字符串。在c语言中我们只能把字符串放在字符串常量以及字符数组中。 1.常见字符串函数 1.1strlen size_t strlen ( const char * str );作用&#xff1a;用来求字符串中 …

人工智能的未来:从 Jetson 到 GPT,沙龙见闻与洞察

前言 在当今数字化时代&#xff0c;人工智能正以惊人的速度改变着我们的生活和工作方式。从智能语音助手到自动驾驶汽车&#xff0c;从智能家居到医疗诊断&#xff0c;人工智能技术已经广泛渗透到各个行业&#xff0c;并为其带来了巨大的变革和创新。越来越多的行业专家、学者…

postman发送图片

POSTMAN 如何发送携带图片的请求? 闲话不叙 步骤如下&#xff1a; 新建一个请求&#xff0c;在Headers中添加一对k-v : Content-Type > multipart/form-data 请求的接口: RequestMapping("/fileUploadController")public String fileUpload(MultipartFile fil…

【C++】构造函数和析构函数第一部分(构造函数和析构函数的作用)--- 2023.9.25

目录 前言初始化和清理的概念构造函数和析构函数的作用构造函数的作用析构函数的作用 使用构造函数和析构函数的注意事项默认的构造函数和析构函数结束语 前言 在使用c语言开发的项目场景中&#xff0c;我们往往会遇到申请空间的需求&#xff0c;同时也肯定遇到过程序运行一段…