【数据结构】堆详解!(图解+源码)

个人头像
🎥 屿小夏 : 个人主页
🔥个人专栏 : 数据结构解析
🌄 莫道桑榆晚,为霞尚满天!

文章目录

  • 🌤️前言
  • 🌤️堆的理论
    • ☁️二叉树的顺序存储
    • ☁️堆的概念
  • 🌤️堆的实现逻辑
    • ☁️堆向下调整算法
    • ☁️建堆
    • ☁️建堆时间复杂度
    • ☁️堆的插入
    • ☁️堆的删除
  • 🌤️堆的代码是实现
    • ☁️堆的结构体
    • ☁️堆的初始化
    • ☁️堆的销毁
    • ☁️堆的插入
    • ☁️堆的删除
    • ☁️取堆顶数据
    • ☁️堆的数据个数
    • ☁️堆的判空
  • 🌤️堆特性总结
  • 🌤️全篇总结

在这里插入图片描述

🌤️前言

堆是一种基本而强大的数据结构。本文将深入探讨堆的概念、原理以及实现。

🌤️堆的理论

☁️二叉树的顺序存储

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

在这里插入图片描述

☁️堆的概念

在这里插入图片描述

堆的性质:

  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。

在这里插入图片描述

🌤️堆的实现逻辑

☁️堆向下调整算法

一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整
成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

int array[] = {27,15,19,18,28,34,65,49,25,37};

在这里插入图片描述

☁️建堆

给定一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,这个时候就需要我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆(向下调整)。

int a[] = {1,5,3,8,7,6};

在这里插入图片描述

☁️建堆时间复杂度

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

在这里插入图片描述

根据上图可以推算出: 建堆的时间复杂度为O(N)。

☁️堆的插入

先插入一个10到数组的尾上,再进行向上调整算法,直到满足堆。

在这里插入图片描述

☁️堆的删除

删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。

在这里插入图片描述

🌤️堆的代码是实现

☁️堆的结构体

typedef int HeapDataType;typedef struct Heap
{HeapDataType* a;int size;  //有效元素int cpciti; //容量
}HP;
  • HeapDataType 定义了堆中元素的数据类型,这里是整数。
  • struct Heap 定义了一个包含堆数据的结构体,包括一个指向堆数组的指针 ,堆的有效元素个数 ,以及堆的容量 。

☁️堆的初始化

void HeapInit(HP* hp)
{assert(hp);hp->a = NULL;hp->size = hp->cpciti = 0;
}

首先使用 断言来确保传入的指针 不为空。然后,将堆数组指针设置为 NULL,将堆的有效元素个数和容量都初始化为 0。

☁️堆的销毁

void HeapDestroy(HP* hp)
{assert(hp);free(hp->a);hp->a = NULL;hp->size = hp->cpciti = 0;
}

使用 断言确保传入的指针 不为空。然后,使用函数释放堆数组分配的内存,并将指针设置为 NULL。最后,将堆的有效元素个数和容量都设置为 0。

☁️堆的插入

void AdjustUp(HeapDataType* a, int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}
void HeapPush(HP* hp, HeapDataType x)
{assert(hp);//if (hp->size == hp->cpciti){int newCapacity = hp->cpciti == 0 ? 4 : hp->cpciti * 2;HeapDataType* tmp = (HeapDataType*)realloc(hp->a, sizeof(HeapDataType) * newCapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}hp->a = tmp;hp->cpciti = newCapacity;}hp->a[hp->size] = x;hp->size++;AdjustUp(hp->a, hp->size-1);
}

AdjustUp用于将堆的最后一个节点(即插入的新节点)向上调整,使得以新节点为叶子节点的子树仍然满足堆的性质。具体步骤如下:

  1. 初始化parent为(child - 1) / 2,即新节点的父节点。
  2. 如果child大于0(即child不是根节点),则执行以下操作:
    • 如果child节点的值小于parent节点的值,则交换child和parent节点的值,并更新child为parent,parent为(child - 1) / 2。
    • 否则,跳出循环。
  3. 调整结束。

HeapPush用于向堆中插入一个新的元素。具体步骤如下:

  1. 检查堆的大小是否达到了容量上限,如果是,则进行扩容操作。
  2. 将新元素x放入堆的最后一个位置。
  3. 堆的大小加1。
  4. 调用AdjustUp函数,将新插入的元素向上调整。

☁️堆的删除

void AdjustDown(HeapDataType* a, int n, int parent)
{int child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child + 1] < a[child]){child++;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}
void HeapPop(HP* hp)
{assert(hp);assert(hp->size > 0);Swap(&hp->a[0], &hp->a[hp->size - 1]);hp->size--;AdjustDown(hp->a,hp->size,0);
}

AdjustDown用于将堆的根节点向下调整,使得以根节点为根的子树仍然满足堆的性质。具体步骤如下:

  1. 初始化child为parent的左孩子节点。
  2. 如果child小于n(即child在数组范围内),则执行以下操作:
    • 如果child+1也小于n且右孩子节点的值小于左孩子节点的值,则将child更新为右孩子节点。
    • 如果child节点的值小于parent节点的值,则交换child和parent节点的值,并更新parent为child,child为parent的左孩子节点。
    • 否则,跳出循环。
  3. 调整结束。

HeapPop用于删除堆的根节点。具体步骤如下:

  1. 交换根节点和最后一个节点的值。
  2. 将堆的大小减1。
  3. 调用AdjustDown函数,将根节点向下调整。

☁️取堆顶数据

HeapDataType HeapTop(HP* hp)
{assert(hp);assert(hp->size > 0);return hp->a[0];
}

断言来确保传入的指针 是非空的(不为 NULL),以及堆的大小大于0。如果这些条件不满足,程序会终止执行。然后,返回堆的顶部元素,也就是堆数组中的第一个元素。

☁️堆的数据个数

int HeapSize(HP* hp)
{return hp->size;
}

size即堆的大小,表示堆中当前包含的元素个数。

☁️堆的判空

int HeapEmpty(HP* hp)
{assert(hp);return hp->size == 0;
}

断言确保传入的指针不为空,检查堆的大小是否等于0。如果堆的大小为0,函数返回1(表示堆为空),否则返回0(表示堆不为空)。

🌤️堆特性总结

  1. 堆是一棵完全二叉树,即除了最后一层外,其他层都是满的,最后一层从左到右填满。
  2. 堆分为大根堆和小根堆两种,大根堆中每个节点的值都大于其子节点的值,小根堆中每个节点的值都小于其子节点的值。
  3. 堆的根节点是堆中的最小(或最大)元素。
  4. 堆中的任意节点的值都小于(或大于)其子节点的值。
  5. 堆中的元素是按照层序遍历的顺序存储在数组中的,可以用数组来实现堆。
  6. 堆的插入和删除操作分别为向上调整(AdjustUp)和向下调整(AdjustDown),保证插入和删除后仍然满足堆的性质。
  7. 堆的时间复杂度为O(logN),其中N为堆中元素的个数。

🌤️全篇总结

堆作为数据结构中的重要部分,展现了在多种算法和应用中的价值。掌握堆的知识会对你以后解决各种问题和优化性能提供重要帮助。
在这里插入图片描述

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

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

相关文章

聚势启新,KaiwuDB 生态联盟沙龙首站落地长春

11月9日&#xff0c;由 KaiwuDB 联合和润集团、致远互联主办的“KaiwuDB 生态联盟沙龙”首站活动在吉林长春顺利举办。沙龙以“聚势&#xff0c;启新”为主题&#xff0c;邀请基础软硬件、应用软件、信息安全等产业链上下游伙伴企业到场&#xff0c;共同就产业数智化趋势下的新…

【milkv】2、mpu6050驱动添加及测试

前言 本章介绍mpu6050的驱动添加以及测试。 其中驱动没有采用sdk提供的驱动&#xff0c;一方面需要配置irq&#xff0c;另一方面可以学习下如何通过ko方式添加驱动。 一、参考文章 驱动及测试文件编译流程&#xff1a; https://community.milkv.io/t/risc-v-milk-v-lsm6ds…

低代码平台是什么?具备哪些特性?

目录 一、低代码开发概念 二、低代码开发和零代码开发的区别 三、低代码和零代码的开发优势 四、低代码开发平台介绍 JNPF开发平台 1&#xff09;产品功能点 2&#xff09;产品功能模块 五、小结 低代码开发平台近两年发展迅猛&#xff0c;并迅速渗透到各个细分领域。本文简要介…

Arduino ESP8266使用AliyunIoTSDK.h连接阿里云物联网平台

文章目录 1、AliyunIoTSDK简介2、相关库安装3、阿里云创建产品&#xff0c;订阅发布4、对开源的Arduino ESP8266源代码修改5、使用阿里云点亮一个LED灯6、设备向阿里云上传温度数据7、项目源码 1、AliyunIoTSDK简介 AliyunIoTSDK是arduino的一个库&#xff0c;可以在arduino的…

并发安全问题之--事物失效问题

并发安全问题之–事物失效问题 事物失效常见的6种原因&#xff1a; 1、事物方法非public修饰 2、非事物方法调用事物方法 3、事物方法抛出的异常被捕获了 4、事物方法抛出的异常类型不对 5、事物传播行为不对&#xff08;事物发生嵌套时有事物传播&#xff09; 6、事物锁属类没…

汽车一键启动智能系统功能作用

在现代科技的推动下&#xff0c;我们的生活每天都在发生着变化。其中&#xff0c;汽车智能一键启动系统就是科技改变生活的最好例子之一。 首先&#xff0c;我们来简单了解一下汽车智能一键启动系统。它是一种利用先进的电子技术和无线通信技术&#xff0c;实现无需钥匙即可启…

关于ruoyi(若依)框架的介绍,若依项目的入门,ruoyi(若依)框架的优缺点

一&#xff0c;关于ruoyi&#xff08;若依&#xff09;框架的介绍 若依&#xff08;Ruoyi&#xff09;框架是一款基于 Spring Boot 2.5.5、Spring Cloud 2020.0、OAuth2 与 JWT 鉴权等核心技术&#xff0c;同时也支持Spring Security、Apache Shiro 等多种安全框架&#xff0c;…

【论文】利用移动性的比例公平蜂窝调度测量和算法

&#xff08;一支笔一包烟&#xff0c;一节论文看一天 &#xff09;&#xff08;一张纸一瓶酒&#xff0c;一道公式推一宿&#xff09; 摘要1. 引言2. 相关工作3. 模型和问题公式4. 预测FPF调度 &#xff08; P F &#xff09; 2 S &#xff08;PF&#xff09;^2S &#xff08;…

【数据仓库】数仓分层方法详解与层次调用规范

文章目录 一. 数仓分层的意义1. 清晰数据结构。2. 减少重复开发3. 方便数据血缘追踪4. 把复杂问题简单化5. 屏蔽原始数据的异常6. 数据仓库的可维护性 二. 如何进行数仓分层&#xff1f;1. ODS层2. DW层2.1. DW层分类2.2. DWD层2.3. DWS 3. ADS层 4、层次调用规范 一. 数仓分层…

【机器学习基础】机器学习入门(2)

&#x1f680;个人主页&#xff1a;为梦而生~ 关注我一起学习吧&#xff01; &#x1f4a1;专栏&#xff1a;机器学习 欢迎订阅&#xff01;后面的内容会越来越有意思~ &#x1f4a1;往期推荐&#xff1a;【机器学习基础】机器学习入门&#xff08;1&#xff09; &#x1f4a1;…

Linux应用层点亮硬件的LED灯

一 应用层操作硬件的两种方法 应用层想要对底层硬件进行操控&#xff0c;通常可以通过两种方式&#xff1a; /dev/目录下的设备文件&#xff08;设备节点&#xff09;&#xff1b;/sys/目录下设备的属性文件。 具体使用哪种方式需要根据不同功能类型设备进行选择&#xff0c;通…

算法——图——bsf 广度优先搜索算法 (Breadth First Search)

图遍历算法——bsf 广度优先搜索算法 &#xff08;Breadth First Search&#xff09; 算法 概述算法过程步骤一&#xff1a;初始化原点到队列步骤二&#xff1a;将队列的头顶点放入到已完成集合步骤三&#xff1a;将订单的关联顶点放入到队列中步骤四&#xff1a;将u顶点设置为…