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

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

文章目录

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

在这里插入图片描述

🌤️前言

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

🌤️堆的理论

☁️二叉树的顺序存储

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

在这里插入图片描述

☁️堆的概念

在这里插入图片描述

堆的性质:

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

在这里插入图片描述

🌤️堆的实现逻辑

☁️堆向下调整算法

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

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.mzph.cn/news/138115.shtml

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

相关文章

路径加密(替换空格),剑指offer,力扣

目录 我们直接看题解吧&#xff1a; 方法&#xff1a; 审题目事例提示&#xff1a; 解题思路&#xff1a; 法1&#xff1a; 代码&#xff08;法1&#xff09;&#xff1a; 法2&#xff1a; 代码&#xff08;法2&#xff09;&#xff1a; 原题解&#xff1a; 【剑指Offer】2、替…

GoLong的学习之路(二十三)进阶,语法之并发(go最重要的特点)(锁,sync包,原子操作)

这章是我并发系列中最后的一章。这章主要讲的是锁。但是也会讲上一章channl遗留下的一些没有讲到的内容。select关键字的用法&#xff0c;以及错误的一些channl用法。废话不多说。。。 文章目录 select多路复用通道错误示例并发安全和锁问题描述互斥锁读写互斥锁 syncsync.Wait…

合成数据如何改变制造业

人工智能正在工厂车间使用&#xff0c;以识别生产线中的低效率。它可以有效地预测设备何时需要维护&#xff0c;以避免停机。人工智能被用于发现产品中的缺陷。 为了完成所有这些工作&#xff0c;使用从人工智能应该学习的过程中收集的数据来创建或训练模型。对于缺陷识别&…

定义无向加权图,并使用Pytorch_geometric实现图卷积

首先定义无向边并定义边的权重 import torch import torch.nn as nn from torch_geometric.nn import GCNConv import torch.nn.functional as F from torch_geometric.data import Dataa torch.LongTensor([0, 0, 1, 1, 2, 2, 3, 4]) b torch.LongTensor([0, 1, 2, 3, 1, 5,…

EXCEL中安装多个vsto插件,插件之间互相影响功能,怎么解决

在 Excel 中安装多个 VSTO 插件&#xff0c;并且这些插件之间存在互相影响的情况下&#xff0c;可以采取以下措施来解决问题&#xff1a; 1. **隔离插件功能&#xff1a;** - 确保每个 VSTO 插件都有清晰的功能和责任范围&#xff0c;避免不同插件之间的功能重叠。这可以通…

【前端】TypeScript核心知识点讲解

1.TypeScript简介及入门案例 &#xff08;1&#xff09;什么是TypeScript&#xff1f; TypeScript 是 JavaScript 的一个超集&#xff0c;支持 ECMAScript 6 &#xff08;ES6&#xff09;标准。 TypeScript 由微软开发的自由和开源的编程语言。 TypeScript 设计目标是开发大…

【IP-guard WebServer 远程命令执行漏洞复现(0day)】

文章目录 一、漏洞说明二、影响版本三、资产测绘四、漏洞复现五、修复建议 一、漏洞说明 IP-guard是由溢信科技股份有限公司开发的一款终端安全管理软件&#xff0c;旨在帮助企业保护终端设备安全、数据安全、管理网络使用和简化IT系统管理。 IP-guard Webserver远程命令执行漏…

Linux服务器上搭建JupyterNotebook教程

搭建需知 1.确保是Linux服务器&#xff1b; 2.已经在linux服务器上安装好anaconda3&#xff1b; 搭建教程 请按照顺序依次执行下面的命令&#xff1a; 1、安装Jupyter Notebook 执行以下命令&#xff0c;安装jupyter notebook conda install jupyter【注】 如果anaconda3…

探秘OpenCV中的findContours函数

文章目录 导言findContours函数的作用函数原型原理分析 应用场景代码示例结语 导言 在计算机视觉领域&#xff0c;图像处理是一项重要的任务。而在图像处理的过程中&#xff0c;轮廓&#xff08;Contours&#xff09;的提取是一项基础且关键的操作。OpenCV库中的findContours函…

Java中内部类

1 非静态内部类 1.1 定义 非静态内部类可以直接在一个类里面定义&#xff0c;只有在外部对象存在时才有意义 1.2 语法 new 外部类().new 内部类() 2 静态内部类 2.1 定义 静态内部类的实例化 不需要一个外部类的实例为基础&#xff0c;可以直接实例化 2.2 语法 new 外…

瑞芯微:基于RK3568的深度估计模型部署

根据单张图像估计深度信息是计算机视觉领域的经典问题&#xff0c;也是一项具有挑战的难题。由于单目图像的尺度不确定&#xff0c;传统方法无法计算深度值。 随着深度学习技术的发展&#xff0c;该范式已经成为了估计单目图像的深度信息的一种解决方案。早期的深度估计方法大多…

PSP - 蛋白质复合物结构预测 Template Pair 特征 Mask 可视化

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/134333419 在蛋白质复合物结构预测中&#xff0c;在 TemplatePairEmbedderMultimer 层中 &#xff0c;构建 Template Pair 特征的源码&#xff0c…

点云从入门到精通技术详解100篇-基于 PoinTr 模型的牙齿点云补全

目录 前言 国内外研究现状 传统点云补全方法 深度学习点云补全方法 存在的难点

【react hook】在react hook组件中,使用Antd Table组件,columns数据异步获取,list数据更新但没有rerender解决办法

情景描述 我们有一个react组件&#xff0c;显示了一个Antd Table组件&#xff0c;设置了一个columns变量并复制给Table的columns属性&#xff0c;由于我们请求的datasource来源是异步的&#xff0c;示例伪代码如下&#xff1a; const [columns, setColumns] useState([]); /…

Java通过javacv获取视频、音频、图片等元数据信息(分辨率、大小、帧等信息)

相信我们都会或多或少需要给前端返回视频或者音频的一些信息&#xff0c;那么今天这篇文章通过Java语言使用javacv来获取视频、音频、图片等元数据信息&#xff08;分辨率、大小、帧等信息&#xff09; 一、首先导入依赖 可以先导入javacv/javacv-platform依赖&#xff0c;由…

【 云原生 | K8S 】Kubernetes 概述

Kubernetes 概述 1 K8S 是什么&#xff1f; K8S 的全称为 Kubernetes (K12345678S)&#xff0c;PS&#xff1a;“嘛&#xff0c;写全称也太累了吧&#xff0c;不如整个缩写”。 作用&#xff1a; 用于自动部署、扩展和管理“容器化&#xff08;containerized&#xff09;应用…

确定性 vs 非确定性:GPT 时代的新编程范式

分享嘉宾 | 王咏刚 责编 | 梦依丹 出品 | 《新程序员》编辑部 在 ChatGPT 所引爆的新一轮编程革命中&#xff0c;自然语言取代编程语言&#xff0c;在只需编写提示词/拍照就能出程序的时代&#xff0c;未来程序员真的会被简化为提示词的编写员吗&#xff1f;通过提示词操纵 …

安卓常见设计模式13------过滤器模式(Kotlin版)

W1 是什么&#xff0c;什么是过滤器模式&#xff1f;​ 过滤器模式&#xff08;Filter Pattern&#xff09;是一种常用的结构型设计模式&#xff0c;用于根据特定条件过滤和筛选数据。 2. W2 为什么&#xff0c;为什么需要使用过滤器模式&#xff0c;能给我们编码带来什么好处…

二分法

文章目录 二分法概述二分 > value最左的位置二分 < value最右的位置局部最小值问题 二分法概述 什么是二分法呢&#xff1f;相信大家都有所了解&#xff0c;举个最经典的二分的例子。 ​ 给定一个整型有序数组&#xff0c;和一个值 v a l u e value value&#xff0c;如…

Docker+K8s基础(重要知识点总结)

目录 一、Docker的核心1&#xff0c;Docker引擎2&#xff0c;Docker基础命令3&#xff0c;单个容器运行多个服务进程4&#xff0c;多个容器运行多个服务进程5&#xff0c;备份在容器中运行的数据库6&#xff0c;在宿主机和容器之间共享数据7&#xff0c;在容器之间共享数据8&am…