堆【数据结构C语言版】【 详解】

news2024/11/22 21:58:07

目录-笔记整理

  • 一、思考
  • 二、堆概念与性质
  • 三、堆的构建、删除、添加
    • 1. 构建
    • 2. 删除
    • 3. 添加
  • 四、复杂度分析
    • 4.1 时间复杂度
    • 4.2 空间复杂度
  • 五、总结

一、思考

设计一种数据结构,来存放整数,要求三个接口:
1)获取序列中的最值(最大或最小)
2)添加元素
3)删除最值(最大或最小)

分析:

1)如果使用无序的线性表来实现,则需要发现获取最值、删除最值都需要遍历全部数据,复杂度为O(n)
2)如果使用有序的线性表,则查找并删除最值是虽是O(1)级别,但插入一个元素要重新进行排序,最好情况也是O(n)
3)如果平衡二叉查找树实现,虽然查找、插入、删除复杂度是O(log<sub>2</sub>n)级别,但其实现程度复杂,功能多,
   对于仅实现三个接口来说是”大炮打蚊子“,得不尝失。而使用”堆“能很好实现三种接口,且时间复杂度较低,
   获取最大值O(1),添加、删除都为O(log<sub>2</sub>n)

(注:这里的堆不是内存模型里的”堆空间“,勿要混淆)

二、堆概念与性质

堆(Heap)是一种数据结构,物理存储采用顺序表,其元素必须满足的性质是:
{ k i ≤ k 2 i + 1 k i ≤ k 2 i 或 { k i ≥ k 2 i + 1 k i ≥ k 2 i \lbrace^{k_i \leq k_{2i}}_{k_i \leq k_{2i+1}} 或 \lbrace^{k_i \geq k_{2i}}_{k_i \geq k_{2i+1}} {kik2i+1kik2i{kik2i+1kik2i
我们称前者为小顶堆(或小根堆),后者为大顶堆(或大根堆)。观察发现,这和完全二叉树的性质5很一样,因此可以逻辑上理解堆为一棵完全二叉树。
例如:序列(5,7,6,9,8,10)满足小根堆的性质在这里插入图片描述
这棵完全二叉树的根元素又叫堆顶元素,也是序列中的最小值,因此,每次查找最小值只需要获取堆顶元素即可,添加一个元素后,需要对堆重新进行调整每个元素满足堆性质,成为一个新堆;删除元素就是堆顶元素出堆,然后重新调整元素位置,直到全部元素满足堆性质,成为一个新堆。

三、堆的构建、删除、添加

1. 构建

(以小顶堆为例)
如何使一个序列变成满足堆性质的序列,并且具有添加、删除、获取最值三大接口?需要思考两个问题
1)如何由该混乱的序列构建一个堆?
2)往堆里添加、删除(删除堆顶)元素后,如何调整剩余元素成为一个新的堆?

已知一个混乱序列(10,8,6,9,7,5),和一个空堆H,然后遍历序列,每遍历一个序列往空堆添加元素,既要考虑每个元素满足堆的性质,又要思考新添加的元素位置(放在 i i i的位置还是 i + 1 i+1 i+1的位置),发现这种代码逻辑很难实现。由堆的性质和完全二叉树的性质类似,把已知的混乱序列逻辑上形象的看成一个完全二叉树。那么问题就转化为如何把一个”混乱“的完全二叉树转变为一棵小根堆对于的完全二叉树
在这里插入图片描述

观察图发现,我们只需要从非叶子 ⌊ n / 2 ⌋ \lfloor n/2\rfloor n/2结点开始,以它为根,对其根以及根的所有后代元素进行调整使其成为一棵小根堆,直到 ⌊ n / 2 ⌋ − 1 \lfloor n/2\rfloor-1 n/21 ~ 1 1 1位置为根元素都调整完毕,构建堆完成。
如何调整?
在这里插入图片描述
(注:假设现在有一个小根堆序列,其对应的完全二叉树如上图所示)
1)堆顶元素输出或出堆,让表尾元素(11)覆盖(5)并删除表尾元素
2)根(11)和其左右孩子(7,6)中最小的比较,发现6比11小,则6和11交换位置,这时以11为根的子树继续调整,10比11小,则互换位置…直到叶子结点(若中途发现根比孩子中最小的还小,则操作结束,该棵子树已经调整好了)
知道了如何调整,那么堆的构建就是从非叶子 ⌊ n / 2 ⌋ \lfloor n/2\rfloor n/2结点为根的树进行调整,直到 ⌊ n / 2 ⌋ − 1 \lfloor n/2\rfloor-1 n/21 ~ 1 1 1位置为根的每棵树都调整一遍,即堆构建完成

typedef int HeapType;
//构建堆,传入一个无序序列和序列长度,时间复杂度为O(n)
HeadType* CreateHeap(HeapType *H,int length){//由无序序列 H构建堆,该序列从索引0开始 
	int s;
	for(s=length/2-1;s>=0;s--){//建立堆
		AdjustHeap(H,s,length);//调整函数
	}
	return H;
}

//堆排序 ---O(nlogn)
void HeapSort(HeapType *H){
	int s;
	int top;
	//堆顶元素出堆,表尾元素覆盖堆顶(删除表尾元素,即空出表尾单元),原堆顶元素放在表尾
	for(s=length;s>1;s--){//进行length-1次出堆,直到堆中只剩一个元素(该元素的索引:0)
		top=H[0];
		H[0]=H[s-1];
		AdjustHeap(H,0,s-1);
		H[s-1]=top;
	} 
}

2. 删除

堆中的删除操作,就是删除堆顶元素,其步骤和上文的如何调整一致,其调整函数如下

void AdjustHeap(HeapType*H,int s,int len){	//s=len/2-1
	//保存以索引s位置元素为根,此时s位置的结点并不满足最小堆的性质,其
	//他所有结点(s到len-1)位置的结点满足小顶堆的性质 
	int rc=H[s];
	int i;
	for(i=2*s+1;i<len;i=2*i+1){//由完全二叉树的性质:孩子结点和双亲结点的索引关系(注:结点位置从索引0开始,因此i=2*s+1而不是i=2*s)
		if(i<len-1&&H[i]>H[i+1])i++;//索引i是s孩子中的最小元素的索引
		if(rc<=H[i]) break;//若索引s处的元素小于孩子中最小的一个,则调整结束
		H[s]=H[i];
		s=i;
	}
	H[s]=rc;
}

3. 添加

往堆中添加元素,就是先把新元素放在堆的末端,再对新元素结点执行向上的操作,即让其和双亲结点比较,若新元素结点小于双亲结点则它们互换位置,若互换后,再于其双亲比较、交换,直到整棵树的根结点(索引为0的元素),若中途遇到双亲小于新元素的,则执行向上的操作结束,添加完毕

void addElement(HeapType *H,HeapType e,int len){//注:这里假设数组长度大(不会越界),len是元素的个数
	int newIndex=len;//新元素的索引
	int i=(newIndex+1)/2-1;//i是新元素的双亲的索引
	int eElem=e;//暂存新元素
	for(i;i>=0;i=(i-1)/2{//向上执行到根(0号结点)
		if(tElem<H[i]){
			H[newIndex]=H[i];
			newIndex=i;
		}else{
			break;	
		}
	}
	H[newIndex]=eElem;
}

四、复杂度分析

4.1 时间复杂度

创建堆,是从非叶子结点 ⌊ n / 2 ⌋ \lfloor n/2\rfloor n/2位置的元素开始到根,要调整的内部结点总数有 ⌊ n / 2 ⌋ \lfloor n/2\rfloor n/2,这些结点分布在多个层次,而构建堆过程中,每次循环都要调用一次调整函数AdjustHeap(),其每次执行调整函,其中需要比较的次数和其结点所在的树中的层次到叶子结点的深度有关(最坏的情况下)。
推导过程如下:
假设已知现在有目标堆是一个满堆,即其对应结点总数为n=2h-1的完全二叉树(也是满二叉树),深度为h,根结点处于第1层。我们处于第h-1层的每个非叶子结点向下调整时最多比较一次,第h-2层的最多比较2次…,第一层的根结点则比较n-1次(注:这里没计入筛选孩子中最小值的那一次比较)。第一层结点数:21-1=1,第二层结点数:22-1…第h-1层的结点总数:2h-2,则总的比较次数S=可能比较抽象,如下表

树的层次结点数比较次数
第1层1h-1
第2层2h-2
第3层22h-3
h-12h-21

则总的比较次数:
S = ( h − 1 ) + 2 ∗ ( h − 2 ) + 2 2 ∗ ( h − 3 ) + . . . + 2 h − 3 ∗ 2 + 2 h − 2 ∗ 1 = 2 h − h − 1 S=(h-1)+2*(h-2)+2^2*(h-3)+...+2^{h-3}*2+2^{h-2}*1=2^h-h-1 S=(h1)+2(h2)+22(h3)+...+2h32+2h21=2hh1

又由于n = 2h - 1,即 h=log2(n+1),则代入上式中,S=n-h,即创建堆时总比较次数S为O(n)级。往往堆排序过程中,就包含建堆(O(n))和排序两个过程,后者每输出一个堆顶元素,则执行一次对根结点的调整(比较的次数规模:O(h)),即时间复杂度为:O(log2n),综合为:O(nlog2n)

4.2 空间复杂度

常数级:O(1)

五、总结

堆排序,在排序过程中使用常数个辅助单元,其建堆时间为O(n),之后执行n-1次对当前的根向下的操作,不管给定的初始序列是有序还是无序,其用堆来排序的最好、最坏、平均时间复杂度均为:O(nlog2n),同时它是一种不稳定的排序,虽然堆排序速度很快,和快速排序时间复杂度一个水平,但其速度却不如快速排序(和时间复杂度的常数因子有关)。快速排序虽然很快,但是最坏的情况下时间复杂度达到O(n2),空间复杂度达到O(n)
(注:一般说某排序算法时间复杂度是多少,通常指平均情况下的)

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

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

相关文章

AI面试指南:AI工具总结评测,助力求职季

AI面试指南&#xff1a;AI工具总结评测&#xff0c;助力求职季 摘要&#xff1a; 在竞争激烈的AI领域秋招季&#xff0c;准备充分并借助高效工具是提升面试通过率的关键。本文主要介绍一些针对秋招的AI面试工具和学习资源&#xff0c;分为简历优化、面试助手、手撕代码练习三个…

Thinkphp/Laravel旅游景区预约系统的设计与实现

目录 技术栈和环境说明具体实现截图设计思路关键技术课题的重点和难点&#xff1a;框架介绍数据访问方式PHP核心代码部分展示代码目录结构解析系统测试详细视频演示源码获取 技术栈和环境说明 采用PHP语言开发&#xff0c;开发环境为phpstudy 开发工具notepad并使用MYSQL数据库…

ue4多个面重叠闪烁

描述&#xff1a;当多个面重叠的时候&#xff0c;出现闪烁。比如有三个面ABC&#xff0c;A在最底下&#xff0c;B在中间&#xff0c;C在最上面。 解决方案&#xff1a; 方案一&#xff1a; 方法&#xff1a;调整位置&#xff0c;A的Z为0&#xff0c;B的Z为0.01&#xff0c;C的…

2021、2022、2023年江苏省“领航杯”_CTF竞赛_MISC/WEB—部分WP

文章目录 一、前言工具及附件分享 二、MICS1、MICS-小明的困惑2、MICS-流量分析3、MISC-神奇的压缩4、MICS-SecertData5、MISC-我要这key有何用6、MICS-黑客流量分析7、MISC-女儿的秘密8、MICS-snow9、MICS-jsfuck 三、WEB1、WEB- ctf_xxe2、WEB- ctf_uuunserialize3、WEB-ctf_…

无心剑七绝《华夏中兴》

七绝华夏中兴 长空万里尽春声 治世群英喜纵横 一代雄才华夏梦 中兴日月照前程 2024年10月1日 平水韵八庚平韵 无心剑的七绝《华夏中兴》通过对自然景观和国家景象的描绘&#xff0c;展现了一种恢弘的气势和对未来的美好愿景。 意境开阔&#xff1a;首句“长空万里尽春声”以广阔…

SpringBoot2(Spring Boot 的Web开发 springMVC 请求处理 参数绑定 常用注解 数据传递 文件上传)

SpringBoot2&#xff08;Spring Boot 的Web开发 springMVC 请求处理 参数绑定 常用注解 数据传递 文件上传&#xff09; 一、Spring Boot的Web开发 1.静态资源映射规则 总结&#xff1a;只要静态资源放在类路径下&#xff1a; called /static (or /public or /resources or …

启动服务并登录MySQL9数据库

【图书推荐】《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;》-CSDN博客 《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;&#xff08;数据库技术丛书&#xff09;》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) Windows平台下安装与配置MyS…

第168天:应急响应-ELK 日志分析系统Yara规则样本识别特征提取规则编写

目录 案例一&#xff1a;ELK 搭建使用-导入文件&监控日志&语法筛选 案例二&#xff1a;Yara 规则使用-规则检测&分析特征&自写规则 案例一&#xff1a;ELK 搭建使用-导入文件&监控日志&语法筛选 该软件是专业分析日志的工具&#xff0c;但是不支持安…

带你0到1之QT编程:二十一、QChart类图表及曲线图的实战指南

此为QT编程的第二十一谈&#xff01;关注我&#xff0c;带你快速学习QT编程的学习路线&#xff01; 每一篇的技术点都是很很重要&#xff01;很重要&#xff01;很重要&#xff01;但不冗余&#xff01; 我们通常采取总-分-总和生活化的讲解方式来阐述一个知识点&#xff01;…

华为OD机试 - 最长元音子串的长度(Python/JS/C/C++ 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

Github 2024-10-01 开源项目月报 Top20

根据Github Trendings的统计,本月(2024-10-01统计)共有20个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量TypeScript项目6Python项目6C项目2JavaScript项目2Rust项目1Shell项目1Ruby项目1HTML项目1Go项目1Jupyter Notebook项目1Lobe Chat: 开源ChatGP…

【C语言】字符和字符串函数(2)

文章目录 一、strncpy函数的使用二、strncat函数的使用三、strncmp函数的使用四、strstr的使用和模拟实现五、strtok函数的使用六、strerr函数的使用 一、strncpy函数的使用 我们之前学习的strcpy的作用是把源字符串拷贝到目标空间内&#xff0c;而且经过我们的模拟实现&#x…

智能招聘系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;企业管理&#xff0c;招聘信息管理&#xff0c;应聘信息管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;招聘信息&#xff0c;我的 开发系统&#…

企望制造ERP系统存在RCE漏洞

漏洞描述 企望制造纸箱业erp系统由深知纸箱行业特点和业务流程的多位IT专家打造&#xff0c;具有国际先进的管理方式&#xff0c;将现代化的管理方式融入erp软件中&#xff0c;让企业分分钟就拥有科学的管理经验。erp的功能包括成本核算、报价定价、订单下达、生产下单、现场管…

鸿蒙NEXT开发环境搭建(基于最新api12稳定版)

注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注&#xff0c;博主会一直更新鸿蒙next相关知识 专栏地址: https://blog.csdn.net/qq_56760790/…

【设计模式-命令】

定义 命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式&#xff0c;它将请求封装为一个对象&#xff0c;从而使您能够使用不同的请求、排队请求或记录请求&#xff0c;并支持可撤销的操作。该模式通过将请求与其执行分离&#xff0c;使得请求者和接收者之间…

养生之道,首先在于饮食!

在快节奏的现代生活中&#xff0c;养生健康成为了人们日益关注的话题。良好的生活习惯和科学的养生方式&#xff0c;不仅能够提升我们的生活质量&#xff0c;还能有效预防疾病&#xff0c;让我们拥有更加充沛的精力和更长久的生命力。 养生之道&#xff0c;首先在于饮食。均衡…

cpp,git,unity学习

c#中的? 1. 空值类型&#xff08;Nullable Types&#xff09; ? 可以用于值类型&#xff08;例如 int、bool 等&#xff09;&#xff0c;使它们可以接受 null。通常&#xff0c;值类型不能为 null&#xff0c;但是通过 ? 可以表示它们是可空的。 int? number null; // …

如何使用 Gradio 创建聊天机器人

如何使用 Gradio 创建聊天机器人 文章目录 如何使用 Gradio 创建聊天机器人一、介绍二、简单示例与实战1、定义聊天功能2、示例&#xff1a;回答“是”或“否”的聊天机器人3、另一个使用用户输入和历史记录的示例4、流式聊天机器人 三、定制化聊天机器人1、为您的机器人添加更…

docker-compose 快速部署clickhouse集群

在本教程中&#xff0c;我们将学习如何使用 Docker Compose 部署一个带有三节点的 ClickHouse 集群&#xff0c;并使用 ZooKeeper 作为分布式协调服务。 前提条件 注意事项&#xff1a; 镜像版本号注意保持一致 [zookeeper:3.7, clickhouse/clickhouse-server:22.5.4]config…