数据结构之堆的创建

news/2024/9/17 7:03:39/ 标签: 数据结构, c语言

1、堆的概念及结构

1.1堆的概念 

如果有一个关键码的集合K={k0,k1,k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足ki<=k2i+1且ki<=k2i+2(或满足ki>=k2i+1且ki>=k2i+2),其中i=0,1,2,…,则称该集合为堆。

小堆:每个父节点不大于子节点

大堆:每个父节点不小于子节点 

堆的性质: 

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

堆总是一棵完全二叉树。

1.2堆的结构

堆的逻辑结构是棵完全二叉树,堆在物理结构上是一个一维数组,所以堆的本质是一个数组。堆又分为大根堆和小根堆。 

大根堆:每个父节点大于等于子节点 

小根堆:每个父节点小于等于子节点

2、堆的实现

堆的实现跟顺序表的实现本质上 是一样的,对堆进行一系列初始化、插入、删除、销毁等操作。但是也有不同之处,就是要实现堆需要两个算法,一个是向上调整算法,一个是向下调整算法。

我们先搞一下堆的向上调整算法和向下调整算法

2.1堆的向上调整算法

向上调整算法经常用在堆的插入中,我们插入数据只能在堆的尾部插入,但是插入之后,堆的性质就会发生变化,就需要我们将插入的尾部数据顺着双亲向上一步一步的调整,成为新的堆

 比如现在有一个小堆,我想插入一个6进去。第一步就是将数据6插入到小堆的尾部,也就是最后一个孩子的后面。

当6插进去之后,你会发现堆的性质发生改变,原来是一个小根堆,每个父亲节点都小于或等于自己的子节点,但插入6之后,就不满足这个性质了,所以就需要我们将6顺着双亲往上调整,让他恢复原本小堆的性质,那么该怎么进行调整呢?这就是我们的第二步了。

我们只需要让插入的新结点与它的父节点进行比较,如果插入的这个节点大于等于它的父节点就不用做改变。如果插入的这个节点小于他的父节点,就让他们两个的节点值交换位置。交换完之后,还需要让父节点与他的父节点进行比较,如果该父节点也小于它的父节点,那就也让这个父节点向上调整,直到调整到堆顶的位置。

注意:未插入数据时,必须是个堆。

 代码演示

 child = parent * 2+1;

 parent = (child-1)/2;

void AdjustUp(HeapDataType* a, int child)//child是下标,插入的尾元素的下标
{//记录插入数据的父节点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;//父亲节点小于子节点,已经成为堆了,不用再比较了。}}
}

2.2堆的向下调整

堆的向下调整经常用到堆的删除操作中,也就是用来删除堆顶元素。向下调整的前提是:除了堆顶元素外,左右子树必须满足堆的性质,也就是左右子树要么全是大堆,要么全是小堆。

除了堆顶元素37不满足小堆的性质外,其它的元素都满足。

以小堆为例: 

  

那么如何将他调成小堆呢?这就需要我们进行向下调整了。 

算法思路:

小堆:找子结点中相对较小的子节点与父节点进行比较 ,如果父节点大于子节点,则两者交换。

大堆:找子结点中相对较大的子节点与父亲比较,如果父节点小于子节点,则进行交换。

堆的向下调整算法,最坏的情况(一直需要交换节点),需要向下调整h-1次,(h为树的高度)。而h = log2(N+1)(N为树的总结点数)。所以堆的向下调整算法的时间复杂度为:O(logN) 。

上面说到,使用向下调整算法,需要满足其的左右子树均为大堆或者小堆,那么我们怎么才能将任意一个数转化为堆呢?我们只需要从倒数第一个非叶子节点开始,从后往前,按下标,作为根,依次向下调整就可以了。

2.3两个数值交换

这个思想大家应该都不陌生

void Swap(int* child, int* parent)
{HeapDataType tmp = *child;*child = *parent;*parent = tmp;
}

2.4堆的初始化 

堆的本质:物理上操纵的是数组,逻辑上操纵的是树。所以堆的初始化跟之前的顺序表差不多,就是把数组置空,数组的容量和有效元素个数都为0。(为什么要断言呢?)

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

2.5堆的销毁

堆的销毁也是跟之前的顺序表一样,因为他们的本质都是数组。所以要销毁堆的话,直接把数组释放掉,再把数组置为空,数组的有效元素个数和容量都为0.

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

2.6堆的插入

 插入一个元素,你要看他的容量是否满了,如果满了,那就扩容,扩完容再插入。没满那就直接插入。插入之后,元素的有效个数就多了一个(size++)。

需要注意的是,这是堆,堆有大堆和小堆。插入元素之后,会有两种情况:一是插入之后堆不受影响,就是直接插入了,该是大堆还是大堆,该是小堆还是小堆。二是插入之后,不是堆了,需要进行向上调整。

void HeapPush(HP* php, HeapDataType x)
{assert(php);
//如果空间满了if (php->size == php->capacity){//如果空间为0,那就开辟四个字节。空间不为0,那就开辟2*capacity个字节int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HeapDataType* tmp = (HeapDataType*)realloc(php->a, newCapacity * sizeof(HeapDataType));if (tmp == NULL){perror("realloc fail");return;}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;//把x插入php->size++;//成功插入数据x,有效元素个数加一AdjiustUp(php->a, php->size - 1);//向上调整算法
}

2.7堆的删除 

堆的删除操作需要用到向下调整。堆的删除是要删除哪一个数据呢?堆的删除删除的是堆顶数据。因为删除堆尾数据没有意义,堆尾的数据不一定是最大的,也不一定是最小的,所以没有任何意义。

大堆:堆顶数据便是最大值。当最大值删除后,我们才可能获得次大的,然后是次次大的,以此类推。

小堆:小堆的堆顶数据是最小的。当堆顶数据删除后,我们可以获取次小的,然后是次次小的,依次类推。

那我们该怎么删除堆顶数据呢?如果直接将堆顶数据删除,剩下的数据就会混乱,那我们该怎么操作呢?

第一步:将堆顶数据与堆尾数据进行交换;

第二步:删除堆尾数据

第三步:让交换上去的堆顶数据向下调整,恢复堆的性质。

这样做堆的左右子树也不会有太大的变化。

void HeapPop(HP* php)
{assert(php);assert(!HeapEmpty(php));//堆不为空时Swap(&php->a[0], &php->a[php->size - 1]);//交换堆中的第一个数据和最后一个数据php->size--;//删除堆尾数据AdjustDown(php->a, php->size, 0);//向下调整数据
}

2.8获取堆顶数据

HeapDataType HeapTop(HP* php)
{assert(php);assert(!HeapEmpty(php));return php->a[0];//获取堆顶数据
}

2.9堆的数据个数

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

2.10堆的判空 

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


http://www.ppmy.cn/news/1522612.html

相关文章

Vue2项目搭建:Vue2.7+Vite4+Pinia+TailwindCSS+Prettier+ESLint

目前 create-vue 和 Vite 都不提供 Vue2 项目的搭建&#xff0c;不想用 Vue CLI 和 webpack&#xff0c;于是就打算从 0 搭建一个工程化项目&#xff0c;支持组合式 API (Composition API) 写法&#xff0c;没有使用 TypeScript&#xff0c;有朋友需要的话我可以再完善一下。 N…

结构体小知识

目录 前言1.结构体数组1.1结构体数组理解1.2结构体数组知识运用1.3 -> 操作符 2. 知识拓展 前言 本期blog是对上一期指针知识的知识补充&#xff0c;如果各位大佬感兴趣的话&#xff0c;可以结合起来一起看&#xff01; 1.结构体数组 1.1结构体数组理解 结构体数组在本…

pytorch torch.nn.functional.one_hot函数介绍

torch.nn.functional.one_hot 是 PyTorch 中用于生成独热编码&#xff08;one-hot encoding&#xff09;张量的函数。独热编码是一种常用的编码方式&#xff0c;特别适用于分类任务或对离散的类别标签进行处理。该函数将整数张量的每个元素转换为一个独热向量。 函数签名 tor…

notepad++软件介绍(含安装包)

Notepad 是一款开源的文本编辑器&#xff0c;主要用于编程和代码编辑。它是一个功能强大的替代品&#xff0c;常常被用来替代 Windows 系统自带的记事本。 Notepad win系统免费下载地址 以下是 Notepad 的一些主要特点和功能&#xff1a; 多语言支持&#xff1a;Notepad 支持多…

Kafka【八】如何保证消息发送的可靠性、重复性、有序性

【1】消息发送的可靠性保证 对于生产者发送的数据&#xff0c;我们有的时候是不关心数据是否已经发送成功的&#xff0c;我们只要发送就可以了。在这种场景中&#xff0c;消息可能会因为某些故障或问题导致丢失&#xff0c;我们将这种情况称之为消息不可靠。虽然消息数据可能会…

proxy代理解决vue中跨域问题

vue.config.js module.exports {...// webpack-dev-server 相关配置devServer: {host: 0.0.0.0,port: port,open: true,proxy: {/api: {target: https://vfadmin.insistence.tech/prod-api,changeOrigin: true,pathRewrite: {//[^ process.env.VUE_APP_BASE_API]: ^/api: / …

【 html+css 绚丽Loading 】000044 两仪穿行轮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽Loading&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495…

【sql】评估数据迁移复杂度调查汇报240904

难度判断标准&#xff1a; - 高难度&#xff1a;使用多个表&#xff08;TBL&#xff09;或有多个join操作的工具 - 低难度&#xff1a;表数量少且没有join操作的简单工具 - 中等难度&#xff1a;介于高低之间&#xff0c;有少量join操作的工具 5. 最后说明不需要仔细…

25届计算机毕业设计:3步打造北部湾助农平台,Java SpringBoot实践

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

AF透明模式/虚拟网线模式组网部署

透明模式组网 实验拓扑 防火墙基本配置 接口配置 eth1 eth3 放通策略 1. 内网用户上班时间&#xff08;9:00-17:00&#xff09;不允许看视频、玩游戏及网上购物&#xff0c;其余时 间访问互联网不受限制&#xff1b;&#xff08;20 分&#xff09; 应用控制策略 2. 互联…

[论文笔记]RAFT: Adapting Language Model to Domain Specific RAG

引言 今天带来一篇结合RAG和微调的论文&#xff1a;RAFT: Adapting Language Model to Domain Specific RAG。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们"。 本文介绍了检索增强微调(Retrieval Augmented Fine Tunin…

【Impala SQL 造数(一)】

前言 SQL 造数即生成测试数据&#xff0c;一般是编码完成之后的测试阶段所需&#xff0c;测试数据可以用于多种目的&#xff0c;包括测试应用程序的功能、业务场景测试、性能测试、数据恢复测试等。在测试阶段特别是数据类需求&#xff0c;需要很多造数场景&#xff0c;像 Hiv…

尚品汇-支付宝支付同步异步回调实现(四十七)

目录&#xff1a; &#xff08;1&#xff09;订单支付码有效时间 &#xff08;2&#xff09;支付后回调—同步回调 &#xff08;3&#xff09;支付宝回调—异步回调 &#xff08;1&#xff09;订单支付码有效时间 &#xff08;2&#xff09;支付后回调—同步回调 static修饰…

【Jupyter Notebook】安装与使用

打开Anaconda Navigator点击"Install"(Launch安装前是Install)点击"Launch"点击"File"-"New"-"Notebook"​ 5.点击"Select"选择Python版本 6.输入测试代码并按"Enter+Shift"运行代码: 代码如下: …

考研系列-408真题数据结构篇(18-23)

写在前面 此文章是本人在备考过程中408真题数据结构部分(2018年-2023年)的易错题及相应的知识点整理,后期复习也尝尝用到,对于知识提炼归纳理解起到了很大的作用,分享出来希望帮助到大家~ # 2018年 1.堆的建立(从后往前进行调整) 2.算法(正整数和数组之间关系的建立)

k8s API资源对象ingress

有了Service之后&#xff0c;我们可以访问这个Service的IP&#xff08;clusterIP&#xff09;来请求对应的Pod&#xff0c;但是这只能是在集群内部访问。 要想让外部用户访问此资源&#xff0c;可以使用NodePort&#xff0c;即在node节点上暴漏一个端口出来&#xff0c;但是这…

pytorch+深度学习实现图像的神经风格迁移

本文的完整代码和部署教程已上传至本人的GitHub仓库&#xff0c;欢迎各位朋友批评指正&#xff01; 1.各代码文件详解 1.1 train.py train.py 文件负责训练神经风格迁移模型。 加载内容和风格图片&#xff1a;使用 utils.load_image 函数加载并预处理内容和风格图片。初始化…

Banana Pi BPI-SM9 AI 计算模组采用算能科技BM1688芯片方案设计

产品概述 香蕉派 Banana Pi BPI-SM9 16-ENC-A3 深度学习计算模组搭载算能科技高集成度处理器 BM1688&#xff0c;功耗低、算力强、接口丰富、兼容性好。支持INT4/INT8/FP16/BF16/FP32混合精度计算&#xff0c;可支持 16 路高清视频实时分析&#xff0c;灵活应对图像、语音、自…

Java中等题-摆动序列(力扣)

如果连续数字之间的差严格地在正数和负数之间交替&#xff0c;则数字序列称为 摆动序列 。第一个差&#xff08;如果存在的话&#xff09;可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。 例如&#xff0c; [1, 7, 4, 9, 2, 5] 是一个 摆动序列 &…

数据库锁之行级锁、记录锁、间隙锁和临键锁

1. 行级锁 InnoDB 引擎支持行级锁&#xff0c;而MyISAM 引擎不支持行级锁&#xff0c;只支持表级锁。行级锁是基于索引实现的。 对于普通的select语句&#xff0c;是不会加记录锁的&#xff0c;因为它属于快照读&#xff0c;通过在MVCC中的undo log版本链实现。如果要在查询时对…