【数据结构】--- 堆

 个人主页:星纭-CSDN博客

系列文章专栏 :数据结构

踏上取经路,比抵达灵山更重要!一起努力一起进步!

目录

一.堆的介绍

二.堆的实现 

1.向下调整算法 

2.堆的创建 

 3.堆的实现

4.堆的初始化和销毁

5.堆的插入 

5.1扩容

5.2向上调整与向下调整

5.2.1向上调整

6.堆的删除 

5.2.2 向下调整

 7.取堆顶数据

8.堆的数据个数

9.判读堆是否为空 


一.堆的介绍

1.1堆的概念以及结构

简单来说,堆总是一个完全二叉树,如果每一个节点都比其子节点的值大就叫大堆,反之就叫做小堆。

二.堆的实现 

1.向下调整算法 

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

首先是27与15和19之间最小的数字交换,也就是与15交换位置。然后再与18和28之间最小的数字交换,即与18交换,再与25交换。

通过这三步,最终得到的就是一个小堆。

2.堆的创建 

下面我们给出一个数组,这个数组逻辑上可以看作一个完全二叉树,当时它还不是一个堆,现在我们通过算法,把他构建成一个堆。可是这个完全二叉树的根节点的左右两个子树也不是堆,如何进行调整呢?只需要从倒数第一个非叶子节点的子树开始调整即可,一直到根节点。 

接下来尝试把下面的数组调整成为一个大堆。 

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

 3.堆的实现

typedef int HPDataType;
typedef struct Heap
{HPDataType* _a;int _size;int _capacity; 
}Heap;
//堆的初始化
void HeapInit(Heap* hp);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);

堆的实现,我们仍然采用多个文件的方式进行,以上是头文件中的代码,也是我们需要完成的函数。

 在上述的代码中的结构体中size表示数组的大小(存入有效数据的个数),capacity表示容量。

4.堆的初始化和销毁

初始化和销毁比较简单。 

初始化:当这个数组没有使用过的时候,其中容量和大小都未0。也没有动态开辟内存。

销毁:释放掉动态开辟的内存,将容量和大小设置为0。

void HeapInit(Heap* hp) {assert(hp);hp->_a = NULL;hp->_capacity = hp->_size = 0;
}void HeapDestory(Heap* hp)
{assert(hp);free(hp->_a);hp->_a = NULL;hp->_capacity = hp->_size = 0;
}

5.堆的插入 

 因为在堆的初始化的时候,是将大小和容量均初始化为0的,所以在插入数据的时候,要判断这个堆中的数组是否需要扩容,由于关于扩容的代码不会经常用到,所以这里就不单独封装函数。

5.1扩容

 当size和capacity相等的时候,就代表此时的数组已满,需要扩容。

	if (hp->_capacity == hp->_size) {int newcapacity = hp->_capacity == 0 ? 4 : hp->_capacity * 2;HPDataType* tmp = realloc(hp->_a,newcapacity * sizeof(HPDataType));if (tmp == NULL) {perror("malloc fail");exit(1);}hp->_a = tmp;hp->_capacity = newcapacity;}

通常情况下扩容都是扩大二倍,当时由于初始化为0了,所以需要判读是否为0,这里采用三目操作符,如果为0,就设置为4,不为0就乘2。

调整动态开辟的内存的大小需要使用realloc函数。

或许有读者疑惑,realloc函数的第一个参数需要填要调整的动态内存块的起始地址。可是在开始的时候hp->_a是NULL。这怎么办呢???

其实realloc函数的第一个参数是NULL的时候,它的功能就和malloc函数的功能一样了,就会直接开辟空间。

第二个参数是内存的大小(单位字节),所以是newcapacity * HPDataType。

最后不要忘记将tmp和newcapacity赋值给hp。

5.2向上调整与向下调整

 将数据拿出来插入到数组的最后一个位置上,可是此时并不能肯定该完全二叉树就是堆。

所以我们需要根据该数据进行调整。

	hp->_a[hp->_size++] = x;AdjustUp();

首先将数据放在数组的最后一个位置上。 所以需要对这个数据进行向上调整。

假设此时我们需要得到一个大堆。

5.2.1向上调整
void AdjustUp(HPDataType* a, int child);

 首先我们知道,使用数组在逻辑上构成完全二叉树时。父亲和孩子的下标是有一定规律的。

	//左孩子 = 父亲 * 2 + 1 //右孩子 = 父亲 * 2 + 2 

由于是向上调整,我们知道的参数只有孩子的下标,又因为整数除法,很容易我们就可以知道父亲节点的下标。

	int parent = (child - 1) / 2;//整数除法

 如果孩子更大就和父亲交换位置。可是交换位置后仍然可能存在不是大堆的情况,所以需要继续调整,直到是大堆为止。

void AdjustUp(HPDataType* a, int child) { //左孩子 = 父亲 * 2 + 1 //右孩子 = 父亲 * 2 + 2 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;}}
}

当child不是0的时候,就代表还可以继续比较,所以结束条件就是child等于0时。或者是parent大于等于0,因为如果parent小于了代表越界了,此时以及不能比较了。

Swap函数是用于交换的。

void Swap(HPDataType* a, HPDataType* b) {HPDataType tmp = *a;*a = *b;*b = tmp;
}

6.堆的删除 

// 堆的删除
void HeapPop(Heap* hp);

堆的删除是指删除堆顶(根位置)的数据 。那么该如何删除呢?

假设一下有这样一个数组并且按照小堆的方式存放。

此时需要删除数据1。如果我们采用向前移动覆盖的方式进行删除。那么会得到一个新的堆 

不难发现此时这个堆的关系全乱了,不再是小堆,而且我们无法轻易的进行更改。所以直接向前移动覆盖这个堆顶的数据是行不通的。

那么该如何删除数据呢?

将数组最后一个数据覆盖到第一个位置上就行了。

 

虽然此时的二叉树仍然不是一个小堆,但是只有堆顶的数据关系不正确而且。此时我们就需要用到向下调整算法来使其重新变成一个小堆。 

5.2.2 向下调整

 首先将最后一个数据放在第一个位置上。

//小堆
void HeapPop(Heap* hp)
{assert(hp);assert(hp->_size > 0);hp->_a[0] = hp->_a[hp->_size - 1];hp->_size--;AdjustDown(hp->_a, hp->_size, 0);
}

然后进行向下调整。

 已知父亲的下标,很轻易可以知道左右两个孩子的下标。然后就是找到两个孩子的最小值与父亲比较。

为了更快速的找到最小的那个孩子的下标,我们可以采用假设法。首先假设左孩子最小。如果右孩子比左孩子小,就可以直接+1,因为右孩子的下标比左孩子刚好大1。

然后就是比较。参考一下代码。

void AdjustDown(HPDataType* a, int size, int parent) {int child = parent * 2 + 1;//假设左孩子最小while (child < size) {if (child + 1 < size && a[child] > a[child + 1]) {++child;}if (a[parent] > a[child]) {Swap(&a[parent],&a[child]);parent = child;child = parent * 2 + 1;}else {break;}}
}

 7.取堆顶数据

这个很简单,就不过多讲解。

HPDataType HeapTop(Heap* hp) {assert(hp);assert(hp->_size > 0);return hp->_a[0];
}

只需要保证此时堆不为空即可。

8.堆的数据个数

int HeapSize(Heap* hp) {assert(hp);return hp->_size;
}

9.判读堆是否为空 

bool HeapEmpty(Heap* hp)
{assert(hp);return hp->_size == 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Bad substitution 奇怪的问题

记得之前写过一篇文章是关于shell 脚本的&#xff0c;这里&#xff0c;当时的系统是 CentOS 的&#xff0c;最近公司把所有的服务器系统都更换为 Ubuntu 了&#xff0c; 结果以前写的那个脚本无法执行了&#xff0c;错误就是 Bad substitution&#xff0c;网上搜索基本都是 {}…

[C++初阶]list类的初步理解

一、标准库的list类 list的底层是一个带哨兵位的双向循环链表结构 对比forward_list的单链表结构&#xff0c;list的迭代器是一个双向迭代器 与vector等顺序结构的容器相比&#xff0c;list在任意位置进行插入删除的效率更好&#xff0c;但是不支持任意位置的随机访问 list是一…

【EIScopus稳检索-高录用】第五届大数据与社会科学国际学术会议(ICBDSS 2024)

大会官网&#xff1a;www.icbdss.org 大会时间&#xff1a;2024年8月16-18日 大会地点&#xff1a;中国-上海 接受/拒稿通知&#xff1a;投稿后1-2周内 收录检索&#xff1a;EI,Scopus *所有参会者现场均可获取参会证明&#xff0c;会议通知&#xff08;邀请函&#xff09;&…

二维码生成需知:名片二维码尺寸多少合适?电子名片二维码制作方法?

随着数字化时代的到来&#xff0c;二维码在各个领域的应用越来越广泛&#xff0c;名片作为商业交流的重要工具之一&#xff0c;也开始逐渐融入二维码的元素。通过在名片上添加二维码&#xff0c;我们可以轻松实现信息的快速传递和分享。然而&#xff0c;名片二维码的尺寸选择成…

【割点 C++BFS】2556. 二进制矩阵中翻转最多一次使路径不连通

本文涉及知识点 割点 图论知识汇总 CBFS算法 LeetCode2556. 二进制矩阵中翻转最多一次使路径不连通 给你一个下标从 0 开始的 m x n 二进制 矩阵 grid 。你可以从一个格子 (row, col) 移动到格子 (row 1, col) 或者 (row, col 1) &#xff0c;前提是前往的格子值为 1 。如…

国产口碑最好的骨传导耳机有哪些?优选五大高口碑机型推荐!

作为一名有着多年工作经验的数码测评师&#xff0c;可以说对骨传导耳机或者蓝牙耳机等数码产品有着深入的了解&#xff0c;近期&#xff0c;有很多粉丝&#xff0c;或者身边的朋友经常向我咨询关于骨传导耳机的问题。确实如此&#xff0c;优质的骨传导耳机能在保护听力、保持环…

HKT DICT解决方案,为您量身打造全方位的一站式信息管理服务

随着大数据时代的到来&#xff0c;企业对现代化管理、数据整合与呈现的解决方案需求不断增长。为满足更多企业客户的多元化信息管理发展需求&#xff0c;香港电讯&#xff08;HKT&#xff09;强势推出全面、高效、安全、可靠的一站式DICT&#xff08;Digital Information and C…

【Python系列】深入解析 Python 中的 JSON 处理工具

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

IDEA常用技巧荟萃:精通开发利器的艺术

1 概述 在现代软件开发的快节奏环境中&#xff0c;掌握一款高效且功能全面的集成开发环境&#xff08;IDE&#xff09;是提升个人和团队生产力的关键。IntelliJ IDEA&#xff0c;作为Java开发者的首选工具之一&#xff0c;不仅提供了丰富的编码辅助功能&#xff0c;还拥有高度…

【NLP学习笔记】transformers中的tokenizer切词时是否返回token_type_ids

结论 先说结论&#xff1a; 是否返回token_type_ids&#xff0c;可以在切词时通过 return_token_type_idsTrue/False指定&#xff0c;指定了True就肯定会返回&#xff0c;指定False&#xff0c;不一定就不返回。 分析 Doc地址 https://huggingface.co/docs/transformers/main…

【电脑应用技巧】如何寻找电脑应用的安装包华为电脑、平板和手机资源交换共享

电脑的初学者可能会直接用【百度】搜索电脑应用程序的安装包&#xff0c;但是这样找到的电脑应用程序安装包经常会被加入木马或者强制捆绑一些不需要的应用装入电脑。 今天告诉大家一个得到干净电脑应用程序安装包的方法&#xff0c;就是用【联想的应用商店】。联想电脑我是一点…

看到指针就头疼?这篇文章让你对指针有更全面的了解!

文章目录 1.什么是指针2.指针和指针类型2.1 指针-整数2.2 指针的解引用 3.野指针3.1为什么会有野指针3.2 如何规避野指针 4.指针运算4.1 指针-整数4.2 指针减指针4.3 指针的关系运算 5.指针与数组6.二级指针7.指针数组 1.什么是指针 指针的两个要点 1.指针是内存中的一个最小单…

智能雷达AI小程序源码系统 销售名片+企业商城+公司动态 带完整的安装代码包以及搭建教程

系统概述 智能雷达AI小程序源码系统是基于先进的AI技术和小程序框架开发的全能型企业级应用。它不仅整合了个人销售名片的便捷分享&#xff0c;还融入了功能丰富的企业商城和实时更新的公司动态展示&#xff0c;实现了从品牌形象塑造到产品销售&#xff0c;再到客户关系维护的…

TransIT-VirusGEN® Transfection Reagent

Mirus转染试剂TransIT-VirusGEN Transfection Reagent&#xff0c;该产品旨在增强载体转染到 贴壁或悬浮的HEK 293细胞的转染效率&#xff0c;并增加重组腺相关病毒或慢病毒的产量。 使用TransIT-VirusGEN转染试剂转染悬浮或贴壁HEK293细胞可获得最高的转染效率。使用不同的转…

【Flask从入门到精通:第一课:flask的基本介绍、flask快速搭建项目并运行】

从0开始入手到上手一个新的框架&#xff0c;应该怎么展开&#xff1f;flask这种轻量级的框架与django这种的重量级框架的区别&#xff1f;针对web开发过程中&#xff0c;常见的数据库ORM的操作。跟着学习flask的过程中&#xff0c;自己去学习和了解一个新的框架&#xff08;San…

常见的过压保护芯片、过压保护的基本参数和选型

过压保护也叫过电压保护&#xff0c;是当电压超过预定的最大值时&#xff0c;使电源断开或使受控设备电压降低的一种保护方式。 过压保护芯片是为了防止输入电压的时候浪涌和波纹过大&#xff0c;导致烧坏后面的元器件芯片。因此过压保护芯片是很有必要的芯片。 常见的过压保护…

经验分享:征信查询多了会不会影响大数据综合评分?

很多人在申请贷款的时候&#xff0c;会有一个疑问&#xff0c;就是自己的征信没逾期&#xff0c;就是查询偏多一点&#xff0c;但能达到申贷要求&#xff0c;为什么还会被拒贷?其实就是大数据花了的原因&#xff0c;那征信查询多了会不会影响大数据综合评分呢?接下来本文就为…

AI自动生成PPT哪个软件好?揭秘5款自动生成PPT的工具

在职场的竞技场上&#xff0c;演示文稿如同战士的利剑&#xff0c;其锋芒直接影响着演讲者的说服力。 然而&#xff0c;制作一份高质量的PPT往往需要耗费大量时间与精力。随着科技的进步&#xff0c;AI自动生成PPT成为了提升效率的新选择。面对市场上琳琅满目的软件&#xff0…

如何给ubuntu虚拟机扩容

虚拟机设置 鼠标点击硬盘&#xff0c;弹出对话框后&#xff0c;点击扩展&#xff0c;输入扩展后的硬盘大小&#xff0c;我这里扩展到100G 安装工具 sudo apt-get install gparted 重新分区

今天,纷享AI正式发布,开启智能CRM新纪元

纷享销客作为国产CRM中连续四年保持近40%增长的领先品牌&#xff0c;一直在探索AICRM领域的数字化变革。 7月10日&#xff0c;纷享AI产品正式上线。与通用大模型不同&#xff0c;纷享AI是在合规之下&#xff0c;开放性的接入各种大模型平台&#xff0c;并结合纷享销客在营销服…