【数据结构与算法】利用堆结构高效解决TopK问题

news2024/12/23 8:19:31

   

            💓 博客主页:倔强的石头的CSDN主页 

           📝Gitee主页:倔强的石头的gitee主页

            ⏩ 文章专栏:《数据结构与算法》

                                  期待您的关注

1b7335aca73b41609b7f05d1d366f476.gif

目录

一、引言

二、堆的基本概念

三、使用堆解决TopK问题

四、算法实现(C语言)

五、性能分析


一、引言

  • TopK问题在给定的一组数据或数据流中,找出最大的K个元素或最小的K个元素。
  • 堆结构简介堆是一种特殊的树形数据结构,每个父节点的值都大于(或小于)其子节点。这种性质使得堆在解决某些问题时非常高效。
  • 堆结构在解决TopK问题中的优势堆能够保持数据的有序性,同时插入和删除操作的时间复杂度较低,同时如果要查找的数据量较大时,将数据载入到内存中进行排序是不可能的,此时就离不开堆了,因此堆是解决TopK问题的理想选择。

二、堆的基本概念

关于堆的详细概念请参考前置文章

【数据结构与算法】探索数组在堆数据结构中的妙用:从原理到实现-CSDN博客

而本篇文章直接在堆的实现文件基础上解决TOPK问题

三、使用堆解决TopK问题

算法思想概述

如果数据较少,可以模仿堆排序(不需要进行完整的堆排序):
对数组建堆,然后堆顶数据与堆尾数据交换,重新调整,循环k次,数组的最后k个数就是要求得前k个最大(最小)的数

 如果数据较大,没有办法将全部数据写入数组,就只能采用我们今天要介绍的算法了:

下面是重点,敲黑板!

分三步走

  • 构建初始堆:从数据集中选择前K个元素并构建初始堆,求最大的k个元素建小堆,求最小的k个元素建大堆
  • 处理数据流并维护堆:对于后续的数据,如果其大于(或小于)堆顶元素,则替换堆顶元素并重新调整堆;否则忽略该数据。       ——堆顶的数据相当于是一个准入门槛,是堆中的最小值。以求最大的k个元素为例,后续遍历的元素只有大于堆顶,才有机会入堆,并且因为堆顶是堆的最小值,不存在较大的数据挡在堆顶的情况
  • 提取TopK元素:堆中的元素即为TopK元素,可以直接输出或进行后续处理。
        

   

    

四、算法实现(C语言)

这里以实现求前k各最大元素为例

之前已经写好的向下调整建小堆算法和交换函数:

void Swap(HPDataType* a, HPDataType* b)//交换函数
{
	HPDataType tmp = *a;
	*a = *b;
	*b = tmp;
}
void Adjustdown(HPDataType* a, int parent, int n)//向下调整算法
{
	assert(a);
	int child = parent * 2 + 1;//先假设左孩子小
	
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] < a[child])//这里以小堆调整为例
			child++;//如果右孩子存在,且右孩子小,父节点与右孩子进行比较
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}

 造数据的过程以及TOPK问题的解决函数:


#if 1
//TOPK问题
void CreateNDate()// 造数据
{
	int n = 1000;
	srand(time(NULL));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");//打开文件写数据
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}

	for (int i = 0; i < n; ++i)
	{
		int x = rand() % 1000000;//写入百万以内的随机数
		fprintf(fin, "%d\n", x);
	}

	fclose(fin);
	fin = NULL;
}

void PrintTopK(int k)//求最大的k个元素
{
	int* arr = (int*)malloc(sizeof(int) * k);//动态申请数组
	const char* file = "data.txt";
	FILE* fin = fopen(file, "r");//打开文件
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}
	for (int i = 0; i < k; i++)//读入k个数据
	{
		fscanf(fin, "%d", &arr[i]);
	}
	for (int i = (k - 2) / 2; i >= 0; i--)//向下调整建堆
	{
		Adjustdown(arr, i, k);
	}

	int x = 0;
	while (fscanf(fin, "%d", &x) != EOF)//从文件读取数据
	{
		if (x > arr[0])//每次与堆顶比对
		{
			arr[0] = x;		//如果比堆顶数据大,覆盖堆顶数据
			Adjustdown(arr, 0, k);	//向下调整恢复堆
		}
	}
	printf("前k个元素:");
	for (int i = 0; i < k; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
#endif
int main()
{
	//test1();
	//CreateNDate();
	PrintTopK(10);
	return 0;
}

 

五、性能分析

  1. 时间复杂度
    • 当处理大数据集时,使用堆来解决TOPK问题可以显著提高性能。具体而言,如果我们使用最小堆来找出前K个最大的元素,或者最大堆来找出前K个最小的元素,时间复杂度可以大致控制在O(NlogK)内,其中N是数据集的大小。这是因为我们需要遍历整个数据集(O(N)),并且在每次插入或删除堆顶元素时,堆都需要进行调整(O(logK))。
    • 相比之下,如果直接使用排序算法(如快速排序或归并排序),其时间复杂度通常为O(NlogN),这在N远大于K时,性能会显著下降。
  2. 空间复杂度
    • 使用堆解决TOPK问题只需要维护一个大小为K的堆,因此空间复杂度为O(K)。这意味着无论数据集的大小如何,我们只需要存储K个元素,这在处理大规模数据集时非常有效。
  3. 算法效率
    • 堆排序是一种原地排序算法(in-place sorting),即只需要使用O(1)的额外空间来进行排序。但是,在使用堆解决TOPK问题时,我们并不直接进行排序,而是利用堆的特性(最大堆或最小堆)来快速找出前K个最大或最小的元素。这种策略在处理TOPK问题时更加高效,因为我们只需要关心前K个元素,而不需要对整个数据集进行排序。
  4. 稳定性
    • 堆排序是一种不稳定的排序算法,因为在调整堆的过程中可能会改变相等元素的相对顺序。但是,在解决TOPK问题时,我们并不关心元素的相对顺序,只关心它们的大小关系。因此,堆的这种不稳定性对于解决TOPK问题并没有太大影响。
  5. 应用场景
    • 使用堆解决TOPK问题在多个领域都有广泛的应用,如搜索引擎、推荐系统、数据分析和数据挖掘等。在这些场景中,我们经常需要从大量数据中快速找出前K个最重要或最高排名的元素,堆排序的优势得以充分体现。

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

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

相关文章

HTTPS基础

目录 1. HTTPS概述2. HTTPS工作原理3. HTTPS证书4. HTTPS安全性特性5. 配置HTTPS示例5.1 获取和配置SSL/TLS证书5.2 示例&#xff1a;在Nginx上配置HTTPS5.3 实施HSTS 6. 结论 1. HTTPS概述 术语描述HTTPS超文本传输安全协议&#xff0c;HTTP的安全版本。SSL/TLS安全套接字层/…

UG NX二次开发(C++)-根据草图创建拉伸特征(UFun+NXOpen)

1、前言 UG NX是基于特征的三维建模软件,其中拉伸特征是一个很重要的特征,有读者问如何根据草图创建拉伸特征,我在这篇博客中讲述一下草图创建拉伸特征的UG NX二次开发方法,感兴趣的可以加入QQ群:749492565,或者在评论区留言。 2、在UG NX中创建草图,然后创建拉伸特征 …

uniapp + vue3 + Script Setup 写法变动 (持续更新)

一、uniapp 应用生命周期&#xff1a; https://uniapp.dcloud.net.cn/tutorial/vue3-composition-api.html 注意&#xff1a; 应用生命周期仅可在App.vue中监听&#xff0c;在其它页面监听无效。 二 、uniapp页面生命周期&#xff1a; https://uniapp.dcloud.net.cn/tutori…

电商控价:系统监测的必要性与优势

在品牌的发展进程中&#xff0c;会遭遇各种各样的渠道问题&#xff0c;控价乃是其中颇为关键的一环。品牌进行控价的目的无疑是为了妥善治理低价链接&#xff0c;低价链接的发现途径可以是人工&#xff0c;也可以是系统。力维网络在为上百个品牌提供服务的过程中察觉到&#xf…

中南大学湘雅三院张如旭/刘爱华团队发现牙髓干细胞来源的外泌体减轻脑缺血再灌注损伤的神经保护机制

随着我国人口老龄化的加剧&#xff0c;中风已成为我国主要的公共卫生疾病之一&#xff0c;确定其潜在的分子机制和治疗靶点对于开发有效的预防和治疗策略至关重要。近期&#xff0c;中南大学湘雅第三医院张如旭、刘爱华团队在经典权威期刊《Pharmacological Research》&#xf…

在 Mac 上使用 MLX 微调微软 phi3 模型

微调大语言模型是常见的需求&#xff0c;由于模型参数量大&#xff0c;即使用 Lora/Qlora 进行微调也需要 GPU 显卡&#xff0c;Mac M系是苹果自己的 GPU&#xff0c;目前主流的框架还在建立在 CUDA 的显卡架构&#xff0c;也就是主要的卡还是来自英伟达。如果要用 Mac 来做训练…

【AI提升】如何使用大模型:本机离线和FastAPI服务调用

大模型本身提供的功能&#xff0c;类似于windows中的一个exe小工具&#xff0c;我们可以本机离线调用然后完成具体的功能&#xff0c;但是别的机器需要访问这个exe是不可行的。常见的做法就是用web容器封装起来&#xff0c;提供一个http接口&#xff0c;然后接口在后端调用这个…

单目行车测距摄像系统(单目测距-行车)

单目行车测距摄像系统是一种利用单个摄像头实现车辆行驶中前方障碍物距离测量的技术。该系统通过计算机视觉算法&#xff0c;能够实时分析摄像头捕捉的图像&#xff0c;精确计算出车辆与前方物体之间的距离&#xff0c;对于自动驾驶、高级驾驶辅助系统&#xff08;ADAS&#xf…

为什么说AI大模型开发人人必备?

首先&#xff0c;能够开发 AGI 时代新应用程序 第一步&#xff1a;学会大模型内核架构&#xff0c;对 Transformer 神经网络架构有个大致的了解&#xff0c;能够搞懂 &#xff1a;LLM 大模型是如何预测下一个 token 的、涌现是如何产生的、幻觉问题如何避免、在线推理的性能问…

德国Testing Expo丨知迪科技Vehicle Bus Tool免费软件“剧透”抢先看!

今日&#xff0c;德国斯图加特汽车测试及质量监控展览会&#xff08;Automotive Testing Expo&#xff09;在斯图加特会展中心正式开幕。作为汽车测试领域专业性最强、影响力最广泛的展会之一&#xff0c;展会首日盛况空前&#xff0c;面向组件和整车的最新测试、开发和验证技术…

CTF实战:从入门到提升

CTF实战&#xff1a;从入门到提升 &#x1f680;前言 没有网络安全就没有国家安全&#xff0c;网络安全不仅关系到国家整体信息安全&#xff0c;也关系到民生安全。近年来&#xff0c;随着全国各行各业信息化的发展&#xff0c;网络与信息安全得到了进一步重视&#xff0c;越…

新的Meta 3D Gen可在一分钟内根据文本生成高质量的3D素材

创建 3D 资产是最耗时、最具挑战性的创意任务之一。如果人工智能助手能够根据文本输入生成三维内容&#xff0c;那么它将使三维内容创作普及化&#xff0c;并对视频游戏和电影行业以及 AR 和 VR 应用程序的开发大有帮助。 Meta 的人工智能研究团队最近推出了 Meta 3D Gen (3DGe…

企业多存储方式如何兼顾安全统一管理、便捷流畅访问的双向需求?

数据和文件存储是企业最基础的需求&#xff0c;常见的存储方式有磁盘存储、NAS存储、SAN存储、云存储、分布式存储、闪存存储等&#xff1b;随着企业规模的扩大、业务结构的复杂化&#xff0c;企业内部可能会同时出现多种存储方式、多个存储设备并行使用的情况。 这样的使用场景…

关于“Unittest”框架中的addtest的方法执行后,所有case都执行的原因分析

原因分析 主要是编译器的问题导致的&#xff0c;因为是unittest框架所有pycharm默认用了unittest框架的方式执行了&#xff0c;所以全部的case就都被执行了&#xff1b;配合入口函数 main 来运行就可以了

少见的更优写法,反转字符串中的元音字母

Leetcode 原题链接 解法一 这道题很简单&#xff0c;令双指针 l l l 和 r r r 从两侧相向移动&#xff0c;交换元音字母即可。但大多人的实现是如下这种可简化的嵌套循环。 如果是 Java 等 String 不可变的语言&#xff0c;应先转换为 CharArray&#xff0c;交换完元音字母…

ubuntu运行qq音乐闪退

ubuntu运行qq音乐闪退 修改/usr/share/applications中的qqmusic.desktop&#xff0c;在Exec后加上 --no-sandbox,如下图所示&#xff1a; 该文件有可能是只读&#xff0c;权限不够的话用sudo vim qqmusic.desktop

Ollama报错:Error: llama runner process has terminated: exit status 0xc0000409

0&#xff0c;背景 今天听说谷歌家的Gemma2性能很好&#xff0c;于是在Ollama上下载到本地测试一下 ollama run gemma2 结果终端里报错 Error: llama runner process has terminated: exit status 0xc0000409 1&#xff0c;原因 原因很简单&#xff0c;新的模型&#xff…

【Linux】应用层创建XXX文件,文件系统调用可以查看到文件名

搞了台电脑&#xff0c;昨天把系统装了下&#xff0c;继续搞事&#xff1a; 上次基于内核代码openat的系统打印被操作的文件名&#xff0c;发现不成功&#xff0c;很奇怪&#xff0c;这种问题内核不可能会犯这种低级别的问题吧&#xff1f; 反过来想&#xff0c;那不是内核的问…

40V转5V,40V转3.3V,40V转3V使用什么降压芯片型号?

40V转5V,40V转3.3V,40V转3V使用什么降压芯片型号? # 40V转5V、3.3V、3V降压芯片&#xff1a;AH8820A的介绍与应用 在电子电路设计中&#xff0c;电压转换是一个常见的需求。特别是在需要将较高电压转换为较低电压以供微控制器、传感器和其他低电压设备使用时&#xff0c;降压…

AI一键音频转文字工具 速度超快,支持实时转换,无需联网,本地整合包下载

这是 CapsWriter-Offline &#xff0c;一个 PC 端的语音输入、字幕转录工具。可用实现简单一键将音频文件转换成文字的懒人工具。 两个功能&#xff1a; 1、实时转换&#xff0c;按下键盘上的 大写锁定键&#xff0c;录音开始&#xff0c;当松开 大写锁定键 时&#xff0c;就会…