【数据结构】深度剖析最优建堆及堆的经典应用 - 堆排列与topk问题

news2024/12/24 22:12:55

🚩纸上得来终觉浅, 绝知此事要躬行。
🌟主页:June-Frost
🚀专栏:数据结构

🔥该文章分别探讨了向上建堆和向下建堆的复杂度和一些堆的经典应用 - 堆排列与topk问题。

❗️该文章内的思想需要用到实现堆结构的一些思想(如向上调整和向下调整等),可以在另一篇文章《堆的顺序实现》中再次了解一下,其中一些接口有具体的实现💖。

目录:

  • 🌍 建堆
    • 🔭 向下建堆
      • ✈️时间复杂度
    • 🔭 向上建堆
      • ✈️时间复杂度
  • 🌎 堆的经典应用
    • 🔭 堆排序
    • 🔭TOPK问题
  • ❤️ 结语

🌍 建堆

 建堆的常见方式有两种:向上建堆和向下建堆。

🔭 向下建堆


 这些交换其实就是向下调整的过程,所以向下建堆只要通过不断的向下调整就可以实现。

    int arr[] = { 10,20,25,35,60,36,15 };
	int n = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(arr, n, i);//向下调整
	}

✈️时间复杂度


 将每层数据个数 * 向下移动的层数求和,得到 T(h) = 2(h-2)*1 + 2(h-3)*2+…+21 *(h-2) + 20 *(h-1) 。通过错位相减,可以得到T(h) = 2h-1-h。因为是满二叉树,所以假设节点为N,则N = 2h - 1 , h = log2(N+1) ,将h换为N,可以得到向下调整建堆合计调整次数 T(N) = N-log(N+1),所以时间复杂度为: O(N) 。


🔭 向上建堆


 同样,这些交换的思想就是向上调整,通过不断调整就可以建堆。

    int i = 0;
	for (i = 1; i < n; i++)
	{
		AdjustUp(arr, i);//向上调整
	}

✈️时间复杂度


将次数求和,T(h) = 21*1 +222+…+2h-2 * (h-2) + 2h-1 * (h-1)。将h与N换算,得到T(N) = -N+(N-1)(log(N+1)-1)+1 ,总的来说,时间复杂度为 O(N * logN) 。


 📙一棵满二叉树的最后一层节点数大概占据总数的50%,向上建堆在最后一层非常吃亏,不仅节点多,调整次数也多,而向下建堆避开了最后一层,时间复杂度也优于向上建堆,所以向下建堆比向上建堆更优


🌎 堆的经典应用

 堆是一种特殊的数据结构,通过大堆和小堆所带来的特性可以使得堆在许多应用中成为非常有效的数据结构。

🔭 堆排序

 堆排序是一种基于堆数据结构的排序算法,它利用了堆的性质来实现高效的排序。对于一个数组,虽然可以通过堆结构来帮助排序,但是实现一整个堆的数据结构并不容易,并且在建堆时会有额外空间的浪费。

例如:通过堆数据结构进行排序

int main()
{
	int arr[] = { 10,20,25,35,60,36,5 };
	HP heap;
	HeapInit(&heap);
	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		HeapPush(&heap, arr[i]);
	}
	int i = 0;
	while (!HeapEmpty(&heap))
	{
		arr[i++] =  HeapTop(&heap);
		HeapPop(&heap);
	}
	HeapDestroy(&heap);
	return 0;
}

  基于这样的一些缺点,所以最好可以实现原地排序。对于任意一个数组是可以看作一个完全二叉树,但是不一定是堆,那么第一个步骤就是建堆。

 类比堆的插入,可以将第一个数组元素看作堆,从第二个元素开始进行向上调整(前提 - 左右子树依旧是堆),这样就可以建堆。

void HeapSort(int* arr, int n)
{
	//第一步:建堆
	int i = 0;
	for (i = 1; i < n; i++)
	{
		AdjustUp(arr, i);
	}
}

⚠注意:

  1. 升序:建大堆。
  2. 降序:建小堆。

📗分析:如果升序建造小堆
在这里插入图片描述
这样的操作代价太大了,为 N*(N*logN),甚至不如直接遍历,丧失了堆的价值。

 当我们想排升序,建造好大堆后,就可以利用堆删除思想来进行排序。

按照这个逻辑不断交换和调整就可以完成排序,时间代价为 N*logN 。

void HeapSort(int* arr, int n)
{
	//第一步:建堆
	int i = 0;
	for (i = 1; i < n; i++)
	{
		AdjustUp(arr, i);//向上调整
	}
	//第二部:排序
	int end = n - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);//交换
		AdjustDown(arr, end, arr[0]);//向下调整
		end--;
	}
}

🔭TOPK问题

 TOPK问题是指从大量数据中获取最大(或最小)的K个数据,例如:有大量的数据,这些数据在内存中存储不下,只能以文件形式存储,现需要找出最大的前k个数。

方式:
1.读取文件前k个数据,在内存数组中建立一个小堆。
2.依次读取剩下的数据,如果大于堆顶元素就进行替换,并向下调整。

当文件的数据全部读取完成后,堆里的数据就是最大的前k个数。

🗼这里以10000个数据为例:

void PrintTok(const char* filename,int k)
{
	//用前k个数据建造一个小堆
	FILE* fout = fopen(filename, "r");
	if (fout == NULL)
	{
		perror("fopen fail");
		exit(-1);
	}
	int* minheap = (int*)malloc(sizeof(int) * k);
	if (minheap == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	int i = 0;
	for (i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &minheap[i]);
	}
	//向下调整建堆
	for (i = (k - 2)/2 ; i >= 0; i--)
	{
		AdjustDown(minheap, k, i);
	}
	//遍历剩下的数据,大的数替代堆顶元素,然后向下调整
	int x = 0;
	while (fscanf(fout, "%d", &x) != EOF)
	{
	    //如果大于堆顶元素就替换
		if (x > minheap[0])
		{
			minheap[0] = x;
			AdjustDown(minheap, k, 0);
		}
	}
	for (i = 0; i < k; i++)
	{
		printf("%d ", minheap[i]);
	}
	printf("\n");
	fclose(fout);
	free(minheap);
}
//造数据
void CreateNDate()
{
	int n = 10000;
	srand((unsigned int)time(NULL));
	const char* file = "Data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen fail");
		exit(-1);
	}
	for (int i = 0; i < n; i++)
	{
		int x = rand() % 1000000;
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}

int main()
{
	//CreateNDate();//造数据
	PrintTok("Data.txt", 10);
	return 0;
}

❤️ 结语

 文章到这里就结束了,如果对你有帮助,你的点赞将会是我的最大动力,如果大家有什么问题或者不同的见解,欢迎大家的留言~

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

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

相关文章

排序学习总结

取每个对象的内接矩形框&#xff0c;然后再排序&#xff0c;根据排序的结果确定原对象顺序。 inner_rectangle1(RegionAffineTrans1, Row1, Column1, Row2, Column2) gen_rectangle1(Rect,Row1, Column1, Row2, Column2) sort_region(Rect,RectSort,character,true, row)count…

Oracle物化视图(Materialized View)

与Oracle普通视图仅存储查询定义不同&#xff0c;物化视图&#xff08;Materialized View&#xff09;会将查询结果"物化"并保存下来&#xff0c;这意味着物化视图会消耗存储空间&#xff0c;物化的数据需要一定的刷新策略才能和基表同步&#xff0c;在使用和管理上比…

【嵌入式】使用嵌入式芯片唯一ID进行程序加密实现

目录 一 背景说明 二 原理介绍 三 设计实现 四 参考资料 一 背景说明 项目程序需要进行加密处理。 考虑利用嵌入式芯片的唯一UID&#xff0c;结合Flash读写来实现。 加密后的程序&#xff0c;可以使得从芯片Flash中读取出来的文件&#xff08;一般为HEX格式&#xff09;不能…

C#WPF命令Command使用实例

本文实例演示C#WPF命令使用实例 定义: 命令(Command):命令表示一个任务单元,并且可跟踪该任务的状态,实际上是实现了ICommand接口的类。然而,命令实际上可以包括任务执行的逻辑代码,也可以不包括从而仅作为联系命令源与命令目标的媒介。比如,WPF 默认的接口实现类Ro…

el-collapse 嵌套中 el-checkbox作为标题,选中复选框与el-tree联动

<el-drawertitle"应用授权":visible.sync"menuDrawer"><el-collapse accordion style"padding: 15px"><el-collapse-item v-for"item in platList"><template slot"title"><el-checkbox v-model…

PostMan、ApiFox等工具Post请求中@RequestParam和@RequestBody的混合使用如何传参

方法签名 PostMapping("/mms/sendAudit")public R sendAudit(RequestParam("mmsId") Long mmsId,RequestParam("ecId") Long ecId,RequestBody(required false) SignMatchRule signMatchRule) {以ApiFox为例子 RequestParam的Params的参数正常…

基于51单片机0.001s精度秒表9.999s仿真设计(源码+仿真+原理图+PCB+报告+器件清单+讲解)

基于51单片机0.001s精度秒表9.999s仿真设计&#xff08;源码仿真原理图PCB报告器件清单讲解 讲解视频1 功能说明&#xff1a;2 仿真电路&#xff1a;3 原理图&#xff1a;4 PCB&#xff1a;5 程序&#xff1a;6 资料清单&&下载链接&#xff1a; 基于51单片机0.001s精度…

2023年【煤气】报名考试及煤气免费试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 煤气报名考试根据新煤气考试大纲要求&#xff0c;安全生产模拟考试一点通将煤气模拟考试试题进行汇编&#xff0c;组成一套煤气全真模拟考试试题&#xff0c;学员可通过煤气免费试题全真模拟&#xff0c;进行煤气自测…

什么是图像翻译

域(Domain)&#xff1a;一系列具有相同风格的图像集合。 图像翻译(Image Translation)&#xff1a;从一张图像到另一张图像的变换&#xff0c;也是域迁移。 &#xff08;风格迁移、图像上色、图像分割...&#xff09; 图像翻译GAN模型分类 根据作用区域&#xff1a;…

Qt学习_13_可执行文件.exe添加图标/logo

本文简单记录一下如何给Qt生成的exe&#xff08;可执行&#xff09;文件&#xff0c;添加图标/logo 第一步 去选一个你喜欢的图标&#xff0c;下载下来 ByteDance IconPark (oceanengine.com) iconfont-阿里巴巴矢量图标库 第二步 用第一步下载的图片&#xff0c;在线生成一…

python 绘制 graphviz

dot 绘图 python 绘制 graphviz 环境 上一节中在本地安装了 graphviz&#xff0c; python 要想使用还需安装 pip 包 pip install graphvizpython 使用 dot Digraph(comment"My Graph") # 添加一些节点 dot.node("A", "Node A") dot.node(&q…

杭州亚运会开幕式惊现数字人火炬手,动捕设备迸发动画制作新动能

在第十九届亚运会开幕式上&#xff0c;首次出现了“数字人”点火形式&#xff0c;打造了亚运史上首个数字点火仪式&#xff0c;这种点火方式是一种颠覆性创作的同时&#xff0c;这也是裸眼3D技术、现实增强和AI人工智能技术的完美结合。 此次数字火炬手的背后是采用了动捕设备&…

九、完整打印立方体贴图的一个面

从上一节可以看出&#xff0c;打印出来的图片是有背景色的&#xff0c;也就是摄像机位置不对。那应该放在哪里呢&#xff1f; 答案是&#xff1a;给定投影矩阵的 fov 为 90 度以捕捉整个面&#xff0c;且摄像机距离该面的距离是立方体边长的一半。 即、 这里我用的立方体是长度…

it运维监控运维方案主要应用在哪些场景

公司越来越依赖IT基础设施。为了保证业务的高效运行&#xff0c;IT运维监控已经成为公司不可或缺的一部分。本文将详细介绍IT运维监控方案&#xff0c;以及如何优化运维效率&#xff0c;并将其应用于各种场景。 IT运维监控方案 监视系统 监控系统是IT运维监控的基础&#xff…

代码随想录 Day6 哈希 LeetcodeT454 四数之和II T383赎金信 T15 三数之和 T18 四数之和

本文代码思路来源于: 代码随想录 前言 希望大家在刷这部分题的时候先熟悉熟悉哈希结构的基本常用api,比较方便理解. LeetCode T454 四数之和 题目链接:454. 四数相加 II - 力扣&#xff08;LeetCode&#xff09; 题目思路 暴力解法仍然是遍历四个数组解决此题, 哈希的思路有…

干货分享 | TSMaster—CCP/XCP标定功能详解

众所周知&#xff0c;CCP是CAN Calibration Protocol CAN 标定协议的缩写&#xff0c;XCP是Universal Measurement and Calibration Protocol 通用测量与标定协议的缩写。二者都普遍使用于开发、测试和车载标定&#xff0c;由ASAM&#xff08;自动化和测量系统标准化协会&#…

k8s--storageClass自动创建PV

文章目录 一、storageClass自动创建PV1.1 安装NFS1.2 创建nfs storageClass1.3 测试自动创建pv 一、storageClass自动创建PV 这里使用NFS实现 1.1 安装NFS 安装nfs-server&#xff1a; sh nfs_install.sh /mnt/data03 10.60.41.0/24nfs_install.sh #!/bin/bash### How to i…

STM32cubeIDE 更改Repository folder

使用STM32CubeIDE时&#xff0c;会调用STM32CubeMX&#xff0c;但是这两个软件下载的更新包都放在C:/user/目录下面&#xff0c;而且文件很大&#xff0c;用不了多久就会把C盘填满&#xff0c;所以刚开始安装的时候就要把更新目录更换掉。具体更换方法如下&#xff1a; Window…

为您的视频编辑应用添加动力,美摄视频剪辑SDK

在当今的数字化时代&#xff0c;视频已经成为了最受欢迎的媒体形式之一。无论是社交媒体平台&#xff0c;还是在线教学站点&#xff0c;甚至是商业广告&#xff0c;都离不开视频的支持。而在这个领域&#xff0c;美摄视频剪辑SDK无疑是您的最佳选择。它不仅功能强大&#xff0c…

基于SSM的学生作业管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…