堆排序之“TOP-K”问题

news2025/3/1 19:00:06

目录

一、什么是TOP-K问题

二、解决思路 

一般的正常思路:

最优的解决思路:

三、文件流中实践TOP-K方法 

创建包含足够多整数的文件:

找出最大的K个数

完整版代码:


前面我已经学习过使用“堆排序”对数组排降序了,接下来再来看一个堆排序的应用场景。

一、什么是TOP-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大,即:N个数找最大的前K个。

二、解决思路 

一般的正常思路:

把这N个数建成大堆,Pop弹出K次堆顶,即可找出最大的前K个数,但是有些场景这种思路解决不了,例如N非常大时,假设N是10亿,10亿个数建堆所需的空间我们来计算一下:

一个整型变量需要四个字节空间,10亿个整型数据需要40亿个字节,1G可以放10亿字节,所以我们需要 4G 空间为10亿个整型数据建堆。 

4G感觉不多的话,如果一百亿数据呢?一千亿呢?

内存无法承载这么大的空间时,数据会存储到磁盘上,磁盘的效率比内存慢很多,所以这种方法如果数据过多,就无法再内存上快速找到TOP-K。

最优的解决思路:

  1. 前K个数建小堆。
  2. 后面N-K个数,依次比较,如果比堆顶的数据大,就替换它进堆,进堆后向下调整。
  3. 最后这个小堆的值就是最大的前K个。

三、文件流中实践TOP-K方法 

我们来在文件中实践一下这个方法:

创建包含足够多整数的文件:

void CreateNData()
{
	int n = 100000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}
	for (size_t i = 0; i < n; i++) {
		int x = rand() % 10000000;
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}
  • 定义一个变量n,表示要生成的随机整数的数量为十万个。
  • 使用srand函数设置随机数种子,time(0)返回当前时间的秒数,确保每次运行程序生成的随机数序列都不同。
  • 定义一个文件名file,表示要生成的文件名。
  • 使用fopen函数打开文件,以写入模式打开,如果没有文件,则创建一个,如果文件打开失败,输出一个错误信息并返回。
  • 使用for循环生成n个随机整数,并使用fprintf函数将它们写入文件中。
  • 使用fclose函数关闭文件。

找出最大的K个数

如果建小堆的方法—向下调整忘记了,不妨看看这篇文章 向下调整的部分

void PrintTopK(int k)
{
	const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error");
		return;
	}
	int* kminheap = (int*)malloc(sizeof(int) * k);
	if (kminheap == NULL)
	{
		perror("malloc error");
		return;
	}

	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &kminheap[i]);
	}
	
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(kminheap, k, i);
	}

	int val = 0;
	while (!feof(fout))
	{
		fscanf(fout, "%d", &val);
		if (val > kminheap[0])
		{
			kminheap[0] = val;
			AdjustDown(kminheap, k, 0);
		}
	}

	for (int i = 0; i < k; i++)
	{
		printf("%d ", kminheap[i]);
	}
	printf("\n");
}

代码较长进行分段讲解: 

    const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error");
		return;
	}
	int* kminheap = (int*)malloc(sizeof(int) * k);
	if (kminheap == NULL)
	{
		perror("malloc error");
		return;
	}
    for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &kminheap[i]);
	}
  • 定义一个文件名file,表示要读取的文件名。
  • 使用fopen函数打开文件,以读取模式打开,如果文件打开失败,输出一个错误信息并返回。
  • 使用malloc函数动态分配一个大小为k的整数数组 kminheap,用于存储最大的 k 个数,如果内存分配失败,输出一个错误信息并返回。
  • 使用for循环从文件中读取前k个整数,并将它们存储到kminheap数组中。
    for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(kminheap, k, i);
	}

	int val = 0;
	while (!feof(fout))
	{
		fscanf(fout, "%d", &val);
		if (val > kminheap[0])
		{
			kminheap[0] = val;
			AdjustDown(kminheap, k, 0);
		}
	}

	for (int i = 0; i < k; i++)
	{
		printf("%d ", kminheap[i]);
	}
	printf("\n");
  • 使用向下调整函数将kminheap数组构建成一个小堆(从小到大)。
  •  feof(fout)是一个C标准库函数,用于判断文件指针fout所指向的文件是否已经到达文件末尾。该函数的返回值为非零值表示已经到达文件末尾,返回值为0表示文件还没有到达末尾。
  • 使用while循环从文件中读取剩余的N-K整数,如果某个整数比堆顶元素大,就将它替换堆顶元素,并使用AdjustDown函数将堆重新调整为小堆,
  • 因为我们需要前K个最大的数,而建的小堆也是K个元素,所以这种操作可以得到由最大前K个元素构成的小堆。
  • 使用for循环输出堆中的所有元素。
  • 使用fclose函数关闭文件。

 我们来测试一下,首先我在主函数调用CreateNData函数对文件data.txt写入文件。

int main()
{
	CreateNData();
	return 0;
}

然后在文件中对五个数添加几位使其成为最大的五个数。

这是屏蔽掉 CreateNData 函数,防止重新生成数字覆盖我修改的数字,调用PrintTopK函数输出5个最大的数。

int main()
{
	//CreateNData();
	PrintTopK(5);
	return 0;
}

输出结果为: 

完整版代码:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void AdjustDown(HPDataType* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size) {
		if (child + 1 < size && a[child + 1] < a[child]) {
			child++;
		}
		if (a[child] < a[parent]) {
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 - 1;
		}
		else {
			break;
		}
	}
}	


void CreateNData()
{
	int n = 100000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}
	for (size_t i = 0; i < n; i++) {
		int x = rand() % 10000000;
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}

void PrintTopK(int k)
{
	const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error");
		return;
	}
	int* kminheap = (int*)malloc(sizeof(int) * k);
	if (kminheap == NULL)
	{
		perror("malloc error");
		return;
	}

	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &kminheap[i]);
	}
	// 建小堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(kminheap, k, i);
	}

	int val = 0;
	while (!feof(fout))
	{
		fscanf(fout, "%d", &val);
		if (val > kminheap[0])
		{
			kminheap[0] = val;
			AdjustDown(kminheap, k, 0);
		}
	}

	for (int i = 0; i < k; i++)
	{
		printf("%d ", kminheap[i]);
	}
	printf("\n");
	fclose(fout);
}

int main()
{
	CreateNData();
	PrintTopK(5);
	return 0;
}

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

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

相关文章

Goby 漏洞发布|F5 BIG-IP AJP 身份认证绕过漏洞(CVE-2023-46747)

漏洞名称&#xff1a;F5 BIG-IP AJP 身份认证绕过漏洞&#xff08;CVE-2023-46747&#xff09; English Name&#xff1a;F5 BIG-IP AJP authentication bypass vulnerability (CVE-2023-46747) CVSS core: 10 影响资产数&#xff1a; 307282 漏洞描述&#xff1a; Cisco …

idea导入git代码

第一种 在本地建一个文件夹&#xff0c;右击 git init git clone https://xxx/xxx.git 发现账号是自己的账号如何改成公司的账号呢&#xff1f;我用的方式是 控制面板 删除后&#xff0c;就可以再次导入代码&#xff0c;就可以输入公司的账号和密码。 第二种 在idea上导入…

比较BFS和DFS

目录 代码框架对比 引出模板 代码框架对比 dfs是栈的递归&#xff0c;bfs是队列的入出。 引出模板 x可以是栈可以是队列&#xff0c;也可以是随机队列、随机容器&#xff0c;一样可以把整张图遍历出来。

Go的命令行工具开发:使用Cobra库

今天我们将深入探讨如何使用Go语言和Cobra库来开发命令行工具。 命令行工具在软件开发中有着广泛的应用&#xff0c;它们快速、高效&#xff0c;且易于自动化。 Go语言因其简洁、高效而被广泛用于命令行工具的开发。Cobra库则是Go中用于构建命令行工具的重要库之一。 为什么选…

Android错题

ctrlo键没有onCrate() 只有继承这个Fragment框架 才能调用ctrlo键onCrate()

比赛准备笔记 --- TensotFlow、软件调试、数据预处理(图像,csv数据)

简介 TensorFlow是由Google团队开发的一个开源深度学习框架&#xff0c;完全基于Python语言设计。它的初衷是以最简单的方式实现机器学习和深度学习的概念&#xff0c;结合了计算代数的优化技术&#xff0c;使计算许多数学表达式变得简单。 优势&#xff1a; 强大的计算能力…

学习笔记二十六:storageclass存储类动态生成存储

storageclass存储类动态生成存储 storageclass作用StorageClass 定义provisioner&#xff1a;供应商reclaimPolicy&#xff1a;回收策略 安装nfs provisioner&#xff0c;用于配合存储类动态生成pv前置条件扩展&#xff1a;什么是sa安装nfs-provisioner程序创建storageclass&am…

DeepSDA

作者未提供代码

会议剪影 | 思腾合力携AI服务器亮相CNCC 2023中国计算机大会

10月26日-28日&#xff0c;第二十届中国计算机大会(CNCC 2023)在沈阳的沈阳新世界博览馆举办。本次大会注册参会观众达到1.3万名&#xff0c;同时还有超过3.6万人次通过CCF视频号直播观看开幕式、特邀报告和大会论坛。直播的4场论坛和活动&#xff0c;人气值达到73万。 “CNCC是…

AR眼镜定制开发-智能眼镜的主板硬件、软件

AR眼镜定制开发是一项复杂而又重要的工作&#xff0c;它需要准备相关的硬件设备和软件。这些设备包括多个传感器、显示装置和处理器等。传感器用于捕捉用户的动作和环境信息&#xff0c;如摄像头、陀螺仪、加速度计等;显示装置则用于将虚拟信息呈现给用户;处理器用于处理和协调…

关于服务端构件模型的典型解决方案

关于服务端构件模型的典型解决方案包括 适用于应用服务器的EJB模型&#xff08;Sun公司J2EE的一部分&#xff09;和COM模型&#xff08;微软公司&#xff09;&#xff0c; 以及适用于Web服务器的servlet模型&#xff08;基于Sun公司JSP技术&#xff09;和Visual Basic及其他技…

NIFI1.23.2_最新版_性能优化通用_技巧积累_使用NIFI表达式过滤表_随时更新---大数据之Nifi工作笔记0063

nifi好用,但是对机器的性能要求也高,如果性能达不到,就会导致,问题发生,比如,队列里显示有内容,但是实际上队列是空的,清也清不掉,只能重启,很麻烦. 关于优化:1.配置前端页面刷新的间隔时间默认30秒,我们可以自己需要看的时候手动刷新我们改成300sec 2.修改CPU阻塞时间,提高CPU…

CSS3网页布局基础

CSS布局始于第2个版本&#xff0c;CSS 2.1把布局分为3种模型&#xff1a;常规流、浮动、绝对定位。CSS 3推出更多布局方案&#xff1a;多列布局、弹性盒、模板层、网格定位、网格层、浮动盒等。本章重点介绍CSS 2.1标准的3种布局模型&#xff0c;它们获得所有浏览器的全面、一致…

【Unity】Addressables资源管理笔记

【Unity】Addressables资源管理笔记 Addressables是一种用于管理资源的系统。允许以一种灵活的方式加载、卸载和管理资源&#xff0c;无论是场景、预制件、材质、纹理、音频剪辑等。 一、快速实现 安装Addressables插件 把对象添加到组 1&#xff09;打开AddressablesGroups面…

本地创建一个虚拟机,并且能够连接到外网

1、从官网下载虚拟机 2、详细的安装教程 点击这里 其中这里的获取&#xff0c;我们店自动IP地址获取就行了&#xff0c;DNS也是自动获取就行了。 4、注意事项 4.1 linux命令:vim: command not found无法使用解决方案, 点击这里

tensorflow-gpu轮子安装

文章目录 简介一、虚拟环境二、安装 TensorFlow-GPU&#xff08;最新&#xff09;版本三、安装 TensorFlow-GPU&#xff08;指定&#xff09;版本四、打印信息4.1、检查当前TensorFlow版本号4.2、检查当前TensorFlow是否支持GPU4.3、检查当前PyTorch是否支持GPU4.4、选择Tensor…

漏洞复现--金蝶云星空 CommonFileServer 任意文件读取

免责声明&#xff1a; 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直…

redis 网课笔记

缓存 缓存雪崩 缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机&#xff0c;导致大量请求到达数据库。 解决方案 给不同的key的TTL添加随机值利于Redis集群提高服务的可用性 哨兵模式、集群模式给缓存业务添加降级限流策略 ngxin或spring cloud gateway给业务…

Python中如何使用pandas和matplotlib库绘制图表

目录 一、引言 二、pandas基础 三、matplotlib基础 四、pandas与matplotlib的结合 五、高级图表与深度分析 结论 本文详细探讨了Python中两个重要的库——pandas和matplotlib&#xff0c;它们在数据处理和可视化中的应用。通过实例代码&#xff0c;我们深入了解了如何利用…

WhatsApp Business账户被封?常见封号原因解析

目前&#xff0c;作为全球即时通讯领域的重要平台之一的WhatsApp已成为企业在营销和与客户沟通时的首选工具。但是长时间、高强度的营销行为很容易导致WhatsApp Business账户突然被封禁&#xff0c;无法再使用账号。即使后续再去进行申诉&#xff0c;要求官方解封该账户&#x…