【数据结构】深度剖析堆排序、TopK问题

news2025/1/12 6:51:31

文章目录

1、TopK问题

主要思路

程序代码

优越性

2.堆排序

主要思路

程序代码

时间复杂度


        堆这个结构实际上还是很有用的,比如TopK问题。

        现在有N个数,要求找最大的前K个。很多人会觉得,这不是很容易吗,排序然后取前K个即可。但是,此时假设N很大,K很小,比如N=100亿,K=10。那么,在这100亿个数字里面,找到最大的前10个,要如何找呢?

        最最重要的是:100亿个数据,实在是太大了,1G 约等于 10亿字节,100亿个数字,就是400亿个字节,则100亿数据约占40G空间。那么,40G在内存中完全开不出来,也就不可能对这么多数据排序。在这里,要用到堆的思想。

1、TopK问题

主要思路

       首先,建立一个有10个数据的小堆(为什么是小堆?)。然后依次遍历数据,只要比堆顶数据大,那么该数据就替换堆顶数据,然后向下调整,重复此过程直到数据遍历结束。最后得到的这个小堆就是100亿个数据里面最大的前10个。

        为什么要建一个小堆呢?而不是大堆?首先要明白,一个堆最能明确的是什么。小堆的堆顶元素,一定是这个堆里面最小的;大堆的堆顶元素,一定是这个堆里面最大的。由于是要寻找最大的前K个数字,那么,建立一个小堆,只要遍历到的数据 A 比堆顶数据 B 大,那么 A 就有资格成为TopK之一,而 B 只能被踢出TopK,相当于用 A 替换 B

        至于数据 A 是TopK里面最大的还是最小的或者中间的,都不知道,所以要向下调整:数据A 在堆顶,其左右子树没变过,都是小堆,所以向下调整之后,堆里面的最小数据到了堆顶。一直遍历,重复此过程直到结束。

        如下,当前小堆的堆顶数据是6,然后遍历到的数据是16,所以16直接替换堆顶数据,然后向下调整,替换之后的小堆里面 最小的数据到了堆顶 ,而新进的数据 16 也到了该在的位置上。并且,向下调整是十分优越的:在K=10的情况下,每一次向下调整算法,最多只需要移动三次数据。

        当那些数据遍历完之后,在内存中建立的这个小堆,自然就是那100亿个数据里面最大的十个。且不一定是有序的,但是唯一能确定的就是:堆顶数据是最大的十个数据里面,最小的那一个。

程序代码

TopK()
{
	FILE* fout = fopen("test.txt", "w");
	if (fout == NULL)
	{
		perror("fopen fail::");
		return;
	}

	int arr[5] = { 0 };  // k个数据的小堆
	for (int i = 0;i < 5;i++)
	{
		fscanf(fout, "%d", &arr[i]);
		AdjustDown(arr, i);
	}

	int val = 0;
	while (fscanf(fout, "%d", &val) != NULL)//一个一个读取,大于堆顶元素,那么把堆顶变成那个数字,并且向下调整
	{
		if (val > arr[0])
		{
			arr[0] = val;
			AdjustDown(arr, 5);
		}
	}
}

优越性

        其优越性具体体现在时间复杂度上面。如下所示,假设也是寻找最大的前K个数字,除去最开始建堆的K个元素,一共要遍历 N-K 次,不同算法在最坏情况下,每次遍历调整的次数不一样,用堆来做是最好的。

2.堆排序

主要思路

        一个堆,只能明确其堆顶元素是最大还是最小,下面的孩子节点数据,根据其实际存储结构来看,未必是有序的。比如下图,是一个小堆,在实际存储结构上,也只能保证其首元素是最小的,后面的那些元素并不是有序的。所以,堆排序就是要把实际存储结构变成有序

        那么,以小堆为例,一个小堆,能确定堆顶是最小的,那么可不可以这样想:建堆之后,把堆顶元素单独取出来,就得到了最小的,存到另外一个数组里,再在剩下的数据里面找最小的,循环此过程,新开的数组里就是升序。可是此时又遇到一个问题了:如下,把堆顶元素取出来之后,剩下的就不是一个堆,那么就需要重新建堆。如果数据量小,其实还好,但是数据多了之后,一直建堆就很麻烦。有没有一种方法可以避免这样呢?

         此时,我们可以参考堆操作里面的删除堆顶元素:把堆尾元素拷贝到堆顶,然后去除堆尾元素,进行向下调整,就能把当前堆的最小数据找出来。如下,先把37拷贝到堆顶,然后去掉堆尾的37,再以此从堆顶进行向下调整。

       去掉堆尾 在其物理结构上就相当于 size-- ,实际上那块空间还在,数据也还在,只不过由于size的原因,取不到那里。那么这时我们思考一下,能不能“废物利用”一下下?把“去掉”的那块空间利用起来,存储什么数据?  之前提到,我们要把修改前的堆顶元素放到一个新数组,那么,我们可不可以把堆顶元素放到去掉的那块空间里呢?

        如下,把修改之前的堆顶元素,放到了被“去掉”的那块空间。下面最右边的那个堆,黑色的才是当前堆,红色是去掉的(画出来只是便于理解)。然后对当前堆(黑色的)进行向下调整,堆顶元素就是当前堆最小的,然后把堆顶元素和当前堆尾元素交换(黑色的)。

        不难想象,一直重复上述操作,最后得到的实际存储结构,从尾到头是从小到大的顺序。那么反过来,正向就是从大到小的顺序,降序。

        当然,如果想要得到升序序列,反着看:在存储结构中,越靠后的数据必然是越大的,所以,要建大堆,大堆的堆顶数据是当前堆里面最大的,堆顶数据从存储结构的末尾依次往前放置,这样子最后得到的就是升序序列。 

程序代码

void Swap(HeapDataType* a, HeapDataType* b)
{
	int c = *a;
	*a = *b;
	*b = c;
}

void HeapSort(HeapDataType* a, int n)
{
	
	//向上调整,时间复杂度是 O(N*log(N))
	// 每层节点少,调整次数少,节点多,调整次数多,且除了根节点,都要调整。
	//for (int i = 1;i < n;i++)
	//{
	//	AdjustUp(a, i);
	//}

	//向下调整, 时间复杂度是 O(N)
	// 每层节点数量多,调整次数少,节点数量少,调整次数多。且跳过最后一层,接近一半的节点不要调整。
	for (int i = (n - 1 - 1) / 2;i >= 0;i--)
	{
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}

}

时间复杂度

       如下:

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

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

相关文章

RabbitMQ之发布确认

目录 8.1. 发布确认 springboot 版本 8.1.1. 确认机制方案 8.1.2. 代码架构图 8.1.3. 配置文件 8.1.4. 添加配置类 8.1.5. 消息生产者 8.1.6. 回调接口 8.1.7. 消息消费者 8.1.8. 结果分析 8.2. 回退消息 8.2.1. Mandatory 参数 8.2.2. 消息生产者代码 8.2.3. 回调…

全国青少年软件编程(Scratch)等级考试二级考试真题2022年3月——持续更新.....

电子学会202203Scratch二级真题及参考答案 1.红框中加入哪个选项积木,不能阻止气球下落?( ) A. B. C. D. 正确答案:D 答案解析: 2.运行下列程序,会画出?( ) A.一条横的实线<

flex布局父项属性

flex布局父项属性 1、flex-direction设置主轴方向 源代码 主轴与侧轴在flex布局中&#xff0c;是分为主轴和侧轴两个方向&#xff0c;同样的叫法有&#xff1a;行和列、x轴和y轴 默认主轴方向就是x轴方向&#xff0c;水平向右 默认侧轴方向就是y轴方向&#xff0c;水平向下 属…

字节京东互联网大厂为什么使用 clickhouse

ClickHouse 开源于 2016 年&#xff0c;是一款开源列式存储的分析型数据库&#xff0c;相较业界 OLAP 数据库系统&#xff0c;其最核心优势就是极致的查询性能。它实现了向量化执行和 SIMD 指令&#xff0c;对内存中的列式数据&#xff0c;一个 batch 调用一次 SIMD 指令&#…

Android-实现View模糊效果

首先&#xff0c;关于在Android上绘图的一句话 在最基本的层面上&#xff0c;Android 上的可视元素&#xff08;如按钮、文本和其他 UI 或自定义元素&#xff09;通常是通过调用 Canvas API 绘制的&#xff0c;如 drawLine()&#xff08;&#xff09;、drawText&#xff08;&a…

从《100道光芒》到《可爱冠军的诞生》:湖南广电打响“参与式”电商突围战

每年的双11&#xff0c;都是各电商平台秀肌肉的比拼时刻。 在这场看不见硝烟的战场上&#xff0c;已经逐步分化成两大阵营&#xff1a;阿里、京东为首的货架电商&#xff0c;抖音为主的兴趣电商。在“人货场”偏向“人”的形势下&#xff0c;它们无一例外都在全力布局吸引“人…

Apache APISIX 玩转 Tongsuo 国密插件

文&#xff5c;罗泽轩Apache APISIX PMC本文通过解读国密的相关内容与标准&#xff0c;呈现了当下国内技术环境中对于国密功能支持的现状。并从 API 网关 Apache APISIX 的角度&#xff0c;带来有关国密的探索与功能呈现。本文 3446 字 阅读 11 分钟1. 什么是国密 顾名思义&…

元素的显示与隐藏

元素的显示与隐藏 本质&#xff1a;让一个元素在页面中隐藏或者显示出来 1、display属性 源代码 display属性用于设置一个元素应如何显示 display: none; 隐藏对象 display: block; block除了转换为块元素之外&#xff0c…

软件测试基础理论体系学习5-静态测试的理解

5-静态测试的理解1 介绍2 静态测试技术2.1 代码检查2.1.1 代码走查2.1.2 编码风格与规范2.1.3 审查2.1.3.1 代码审查和代码走查2.1.3.2 代码审查清单2.2 静态结构分析2.3 代码质量度量1 介绍 静态测试包括包括代码检查、静态结构分析、代码质量度量等。它可以由人工进行&#…

友芝友生物冲刺港交所上市:极其依赖单一供应商,周鹏飞为董事长

12月9日&#xff0c;Wuhan YZY Biopharma Co.,LTD.&#xff08;武汉友芝友生物制药股份有限公司&#xff0c;下称“友芝友生物”&#xff09;向港交所提交上市申请材料&#xff0c;计划在港交所主板上市&#xff0c;中信建投国际为其独家保荐人。 此前&#xff0c;贝多财经曾在…

Spark零基础入门实战(六)使用IntelliJ IDEA创建Scala项目

IntelliJ IDEA(简称IDEA)是一款支持Java、Scala和Groovy等语言的开发工具,主要用于企业应用、移动应用和Web应用的开发。IDEA在业界被公认为是很好的Java开发工具,尤其是智能代码助手、代码自动提示、重构、J2EE支持等功能非常强大。 在IDEA中安装Scala插件 在IDEA中安装…

【Keras计算机视觉OCR】文字识别算法中DenseNet、LSTM、CTC、Attention的讲解(图文解释 超详细)

觉得有帮助麻烦点赞关注收藏~~~ 一、OCR文字识别的概念 利用计算机自动识别字符的技术&#xff0c;是模式识别应用的一个重要领域。人们在生产和生活中&#xff0c;要处理大量的文字、报表和文本。为了减轻人们的劳动&#xff0c;提高处理效率&#xff0c;从上世纪50年代起就开…

GPR学术报告学习-刘四新 主持

钱荣毅 道路空洞 1 设备 天线矩阵 不能完全覆盖调查区域&#xff0c; 存在差异&#xff1a; 难以发现动态发育特征&#xff0c; 数据采集难以实现3D 全覆盖&#xff0c; 高频上探测深度浅&#xff0c; 通道间存在差异。 数据需要校准。。。主要A-SCAN的地面抖动矫正。 异常区…

【EmonCMS】开源数据处理平台安装教程

EmonCMS 安装教程1.EmonCMS平台介绍&#xff1a;2. EmonCMS安装2.1在 Ubuntu中安装2.2.在树莓派中安装3.安装完毕Emoncms是作为该项目的一部分开发的开源web应用程序&#xff0c;用于处理、记录和可视化能源、温度和其他环境数据。Emoncms从OpenEnergyMonitor监控硬件接收数据&…

VS Code 上已有200万+ Java 开发者!

大家好&#xff0c;我们很高兴与大家分享一个好消息&#xff0c;现在 Visual Studio Code 上已有超过200万 Java 开发者&#xff0c;这要来自于长期社区以及用户的支持&#xff0c;所以谢谢你们&#xff01; 对于11月的更新&#xff0c;我们为您带来了全新的代码编辑功能&…

运行 Jmeter 文件生成 HTML 测试报告,我选择 ANT 工具

概述 ant 是一个将软件编译、测试、部署等步骤联系在一起加以自动化的一个工具&#xff0c;大多用于 Java 环境中的软件开发。 在与 Jmeter 生成的 jmx 文件配合使用中&#xff0c;ant 会完成jmx计划的执行和生成jtl文件&#xff0c;并将jtl文件转化为html页面进行查看。 还可…

类加载器和反射

1.通过全限定名&#xff1a;包名类名&#xff08;类名可能会重复&#xff0c;所以要加包名&#xff09;&#xff0c;然后用流拷贝到内存中&#xff0c;并在内存中创建一个class对象用来存储这个class文件的成员信息 类加载过程http://t.csdn.cn/FDnaa 在验证之前会把该类中引…

CRM的开发

开发步骤1.首页的开发2.由首页跳转到登陆页面3.用户登录4.记住密码实现5.安全退出6.登录验证7.创建市场活动8.分页查询市场活动9.删除市场活动10.修改市场活动11.批量导出市场活动12.导入市场活动13.查看市场活动明细14.添加市场活动备注15.删除市场活动备注16.修改市场活动备注…

分分钟搭建出nginx的https服务

nginx添加https协议一、https是什么&#xff1f;二、创建步骤1.安装nginx2.创建证书三、验证四、浏览器访问在windows安装证书总结nginx 192.168.122.50一、https是什么&#xff1f; HTTPS &#xff08;全称&#xff1a;Hypertext Transfer Protocol Secure &#xff09;&…

分布式文件存储系统Minio,tuling,fox,p3 5:27

MinIO | 高性能&#xff0c;对Kubernetes友好的对象存储 一 分布式文件系统应用场景 fastDFS 缺点&#xff1a; 部署麻烦。因为文件名是自动生成的&#xff0c;所以如果要处理文件的话&#xff0c;还要经过额外的编码。Minio介绍 应用场景 互联网海量非结构化数据的存储需求…