【数据结构】堆的模拟实现

前言:前面我们学习了顺序表、单链表、栈、队列,今天我们就开始新的学习吧,今天我们将进入堆的学习!(最近博主处于低谷期)一起加油吧各位。

💖 博主CSDN主页:卫卫卫的个人主页 💞
👉 专栏分类:数据结构 👈
💯代码仓库:卫卫周大胖的学习日记💫
💪关注博主和博主一起学习!一起努力!
在这里插入图片描述


学前必读:

  1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是 ,则它就是满二叉树。
  2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。(至于二叉树,博主会在下一篇博客中给大家讲解,各位友友不用心急)

什么是堆

堆(Heap):我们可以通俗的理解成一种二叉树,但最大值或最小值是存在上面的,且堆中某个节点的值总是不大于或不小于其父节点的值。并且堆总是一棵完全二叉树。光看文字我们可能无法很清晰的理解堆,我们来看下图。
在这里插入图片描述


堆的基本功能

  1. 插入数据
  2. 取堆顶的数据
  3. 删除根节点(最顶部)的数据
  4. 堆的数据个数
  5. 堆的判空

堆的功能实现

思路导读:要想实现堆,我们首先得定义一个结构体,里面存放一个指针,和一个size记录堆中数据个数,一个capacity记录空间的容量看是否需要扩容。如果有不懂的友友可以去看我前面的文章。
代码实现

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

堆的初始化

思路导读:堆的初始化还是一样的,我们把指针置为空,元素个数清零,空间容量清零即可。
代码实现

void HeapInit(HP* php)//堆的初始化
{assert(php);php->a = NULL;php->capacity = 0;php->size = 0;
}

堆的销毁

思路导读:堆的销毁方法也不变,只需要将该指针开辟的空间释放掉,然后将指针置为空,元素个数清零,空间容量清零即可。
代码实现

typedef int HPDataType;//定义一个变量void HeapDestory(HP* php)//堆的销毁
{assert(php);free(php->a);php->a = NULL;php->capacity = 0;php->size = 0;
}

堆的插入数据

思路导读:首先我们在插入的数据应该考虑,(假设我们建的堆是小堆)是直接插入在堆的头部还是在尾部,首先我们来看看头部是不是可行的,如果我们插入在头部,当插入的这个数是小于头部这个数的时候,那么我们就需要调整整个堆中的数据,来一一判断,可想而知这种方式是多么的麻烦,并且时间复杂度也相对来说较高。那么我们考虑把这个数据放在堆的尾部的时候看看是什么情况,我们将堆的数据放在尾部如果比它的父亲结点还小那么我们就交换它和父亲结点的位置即可。我们可以通过下图来对比分析。在这里插入图片描述
尾部插入代码实现:当然这个还是比较简单的,不懂的可以看前面的文章

void HeapPush(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 fail");return;}php->a = tmp;php->capacity = newcapacity;}php->a[php->size] = x;//先存放在尾部
}

我们通过图可知道,我们将数据放在尾部插入,然后将数据和它的父亲结点比较,如果比它的父亲结点还要小,我们就将它们俩俩交换,如果一直没有找到那么什么时候停止呢?就是当走到下标为0的时候停止(如下图)。因此我们要写一个函数将插入的数据向上调整,来保证我们的堆在插入后仍然成立。
在这里插入图片描述
代码实现:

void AdjustUp(HPDataType* a, int child)//向上调整
{assert(a);int parent = (child - 1) / 2;//父亲节点的下标while (child > 0){if (a[child] < a[parent])//当孩子节点比父亲还小{Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}

整体插入代码实现:


void Swap(int* x, int* y)//交换
{int tmp = 0;tmp = *x;*x = *y;*y = tmp;
}void AdjustUp(HPDataType* a, int child)//向上调整
{assert(a);int parent = (child - 1) / 2;//父亲节点的下标while (child > 0){if (a[child] < a[parent])//当孩子节点比父亲还小{Swap(&a[child], &a[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 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newcapacity);if (tmp == NULL){perror("realloc fail");return;}php->a = tmp;php->capacity = newcapacity;}php->a[php->size] = x;//先存放在尾部AdjustUp(php->a,php->size);php->size++;
}

取堆顶的数据

思路导读:因为我们堆顶的数据在数组中存储的时候就放在了数组的头部,因此我们只需要返回数组下标为0的那个数即可。
代码实现

HPDataType HeapTop(HP* php)// 取堆顶的数据
{assert(php);return php->a[0];
}

删除根节点(堆顶)的数据

思路导读:1.如何删除根节点我们删除根节点我们首先想到的就是直接删除头部的节点,我们来试想一下直接删除根节点,如果直接删除头部节点,那么我们整个堆的数据都要立刻开始动(即数据都往前诺一位)在不考虑删除堆后是否还是堆,因此直接删除根节点是十分麻烦的。我们换一个思路,我们将根节点,和尾部节点的数据交换位置,然后直接尾删数据,那么我们整个堆在不考虑交换后是否仍然是堆的情况下,即可很快速的完成删除根节点。
2. 向下调整我们在完成删除根部节点之后,我们尾部的数据放在了根部,我们自然而然的就要考虑这个树是否仍然是一个堆了,因此我们要把交换后的这个数与它的孩子节点比较,如果比它们大即交换数据,一直到比它们小为止,或者走到树的尾部。如下图所示
在这里插入图片描述
代码实现

void AdjustDown(HPDataType* a, int size, int parent)//向下调整
{assert(a);int child = parent * 2 + 1;//找到第一个孩子while (child < size){//看俩个孩子谁更小,交小的那个与父亲去比较if (a[child] > a[child + 1] && (child+1) < size){child += 1;}if (a[parent] > a[child]){Swap(&a[parent], &a[child]);parent = child;//让父亲和儿子往下走child = child * 2 + 1;}else{break;}}
}void HeapPop(HP* php)//删除根节点(最顶部)的数据
{assert(php);assert(php->size > 0);Swap(&php->a[php->size - 1], &php->a[0]);php->size--;AdjustDown(php->a, php->size, 0);
}

堆的数据个数

思路导读:我们在前面记录了一个size负责记录数据的个数,因此我们只需要直接返回size即可。
代码实现:

size_t HeapSize(HP* php) //堆的数据个数
{assert(php);return php->size;
}

堆的判空

思路导读:我们只需要判断size中的数据是否为0即可。
代码实现:


bool HeapEmpty(HP* php) //堆的判空
{assert(php);return php->size == 0;}

整体函数测试

void Test1()
{int array[] = { 27,15,19,18,28,34,65,49,25,37 };HP hp;HeapInit(&hp);for (int i = 0; i < sizeof(array) / sizeof(int); i++){HeapPush(&hp, array[i]);//插入数据}int k = HeapSize(&hp);while (k--){printf("%d ",HeapTop(&hp));//获取堆顶数据HeapPop(&hp);}//Print(&hp);HeapDestory(&hp);}int main()
{Test1();
}

运行结果:在这里插入图片描述
自己手动排一下,看看是否建堆成功。
在这里插入图片描述


结语:今天的内容就到这里吧,谢谢各位的观看,如果有讲的不好的地方也请各位多多指出,作者每一条评论都会读的,谢谢各位。


🫵🫵🫵 祝各位接下来好运连连 💞

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

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

相关文章

【代码随想录】刷题笔记Day34

前言 考过概率论&#xff0c;发过一场烧&#xff0c;兜兜转转又一月&#xff0c;轻舟已撞万重山&#xff0c;赶紧刷题 贪心算法理论基础 贪心的本质&#xff1a;局部最优→全局最优无套路&#xff0c;常识性推导 举反例 455. 分发饼干 - 力扣&#xff08;LeetCode&#xf…

小黑独自去逛城墙和夫子庙,夜泊秦淮被雨淋,南京马拉松323刷新自己最好成绩,开始参加部里公务员培训的leetcode之旅:12. 整数转罗马数字

小黑代码 class Solution:def intToRoman(self, num: int) -> str:# 定义字符数组和数字数组chars [M, CM, D, CD, C, XC, L, XL, X, IX, V, IV,I]nums [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]total numres # 遍历每个符号for i in range(13):# 依次进…

自炫锁2-b

1. 自旋锁 自旋锁也是为实现保护共享资源而提出一种锁机制。其实&#xff0c;自旋锁与互斥锁比较类似&#xff0c;它们都是为了解决对某项资源的互斥使用。 无论是互斥锁&#xff0c;还是自旋锁&#xff0c;在任何时刻&#xff0c;最多只能有一个保持者&#xff0c;也就说&…

进程(IPC)_D3(2023-12-12)

XMind&#xff08;分图版&#xff09;

三层交换原理

三层交换机出现的背景 早期的网络中一般使用二层交换机来搭建局域网&#xff0c;而不同局域网之间的网络互通由路由器来完成。那时的网络流量&#xff0c;局域网内部的流量占了绝大部分&#xff0c;而网络间的通信访问量比较少&#xff0c;使用少量路由器已经足够应付了。 但…

js 根据指定日期格式化输出( 刚刚,几分钟前,几小时前,今天,标准日期)

代码如下&#xff1a; function getDateStr(stringTime) {let str stringTime;//将字符串转换成时间格式let timePublish new Date(str);let timeNow new Date();let minute 1000 * 60;let hour minute * 60;let day hour * 24;let month day * 30;let year month * 1…

智能优化算法应用:基于闪电搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于闪电搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于闪电搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.闪电搜索算法4.实验参数设定5.算法结果6.…

QML WebEngineView 全屏和退出

作者: 一去、二三里 个人微信号: iwaleon 微信公众号: 高效程序员 在使用浏览器时,我们经常会用到全屏模式,最常见的场景有:观看视频、阅读文章、在线演示等。全屏模式的优点在于,它可以让用户充分地利用有限的屏幕空间,更好地专注于内容本身,从而提供丰富的沉浸式视觉…

渲染农场对工业产品渲染带来的意义与优势?

随着科技的进步&#xff0c;利用精细渲染图来呈现和推广工业设计的创新已成为行业标准。这些图像在产品研发、设计评审和营销阶段起着关键作用&#xff0c;同时对产品最终的成功也产生深远影响。然而&#xff0c;由于产品设计日渐复杂&#xff0c;制作渲染图的任务变得极具挑战…

Qt开发 之 Qt5各版本情况分析

文章目录 1、简介2、Qt5 版本归纳3、下载地址3.1、典型版本3.1.1、Qt5.0.03.1.2、Qt5.9.93.1.3、Qt5.12.12 3.2、当前Qt5最新版本 1、简介 Qt6 出生刚刚好一年的时间&#xff0c;已经出到6.6版本&#xff0c;带来了许多的新特性和改进。今天刚刚好抽空总结下陪伴 我工作这么长…

单元测试技术

文章目录 一、单元测试快速入门二、单元测试断言三、Junit框架的常用注解 一、单元测试快速入门 所谓单元测试&#xff0c;就是针对最小的功能单元&#xff0c;编写测试代码对其进行正确性测试。 常规的例如如果在main中测试&#xff0c;比如说我们写了一个学生管理系统&…

【C语言】数据结构——小堆实例探究

&#x1f497;个人主页&#x1f497; ⭐个人专栏——数据结构学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 导读&#xff1a; 我们在前面学习了单链表和顺序表&#xff0c;以及栈和队列。 今天我们来学习小堆。 关注博主或是订阅专栏&a…

ubuntu如何远程ssh登录Windows环境并执行测试命令

ubuntu如何远程ssh登录Windows环境并执行测试命令 1 paramiko模块简介1.1 安装paramiko1.2 paramiko基本用法1.2.1 创建SSHClient实例1.2.2 设置主机密钥策略1.2.3 连接SSH服务器1.2.4 执行命令1.2.5 关闭SSH连接1.2.6 异常处理 2 windows的配置2.1 启动OpenSSH服务2.2 配置防火…

使用kubeadm部署一套Kubernetes v1.23.0集群

使用kubeadm部署一套Kubernetes v1.23.0集群 1、前置知识点 1.1 生产环境可部署Kubernetes集群的两种方式 目前生产部署Kubernetes集群主要有两种方式&#xff1a; • kubeadm Kubeadm是一个K8s部署工具&#xff0c;提供kubeadm init和kubeadm join&#xff0c;用于快速部…

PHP操作ZIP之ZipArchive类以及如何避免生成压缩文件带有目录层级的问题

常用的方法 php ZipArchive可以说是php自带的一个函数了&#xff0c;他可对对文件进行压缩与解压缩处理&#xff0c;但是使用此类之前我们必须在php.ini中把extensionphp_zip.dll前面的分号有没有去掉&#xff0c;然后再重启Apache这样才能使用这个类库。 ziparchive 可选参数…

Keil 编译输出信息分析:Program size: Code, RO-data , RW-data, ZI-data

一般 MCU 包含的存储空间有&#xff1a;片内 Flash 与片内 RAM&#xff0c;RAM 相当于内存&#xff0c;Flash 相当于硬盘。编译器会将一个程序分类为好几个部分&#xff0c;分别存储在 MCU 不同的存储区。 如图所示&#xff0c;在Keil中编译工程成功后&#xff0c;在下面的Bul…

k8s详细教程(二)

—————————————————————————————————————————————— 博主介绍&#xff1a;Java领域优质创作者,博客之星城市赛道TOP20、专注于前端流行技术框架、Java后端技术领域、项目实战运维以及GIS地理信息领域。 &#x1f345;文末获取源码…

05-详解Nacos配置管理中心,配置拉取的方式,热更新,配置共享(优先级)的步骤

Nacos配置管理 新建配置文件 当微服务部署的实例越来越多时,如果需要修改微服务的配置就需要逐个修改配置文件并且还要重启关联的微服务十分繁琐还易出错 项目中的配置文件分为每个项目特有的配置,项目所公用的配置 每个项目特有的配置: 有些项目中需要但有些项目中又不需要…

源码角度简单介绍LinkedList

LinkedList是一种常见的数据结构&#xff0c;但是大多数开发者并不了解其底层实现原理&#xff0c;以至于存在很多误解&#xff0c;在这篇文章中&#xff0c;将带大家一块深入剖析LinkedList的源码&#xff0c;并为你揭露它们背后的真相。首先想几个问题&#xff0c;例如&#…

C++初阶-string类的模拟实现

string类的模拟实现 一、经典的string类问题1.1 构造函数1.1.1 全缺省的构造函数 2.1 拷贝构造3.1 赋值4.1 析构函数5.1 c_str6.1 operator[]7.1 size8.1 capacity9.1 比较&#xff08;ASCII&#xff09;大小10.1 resize11.1 reserve12.1 push_back(尾插字符)13.1 append(尾插字…