Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【初探数据结构】堆的应用实例(堆排序与TopK问题)

【初探数据结构】堆的应用实例(堆排序与TopK问题)

作者头像
我想吃余
发布于 2025-03-31 10:20:31
发布于 2025-03-31 10:20:31
17800
代码可运行
举报
运行总次数:0
代码可运行
一、引言

堆是一种高效处理优先级问题的数据结构,尤其在动态数据流和排序场景中表现优异。本文将通过堆排序TopK问题两个经典案例,深入解析堆的实际应用,并提供清晰的代码实现与原理分析。

二、堆排序:将无序数组变为有序序列
1. 堆排序的核心思想
  • 利用最大堆的性质:堆顶元素始终为最大值。
  • 两步走策略
    1. 建堆
    • 升序 :建大堆
    • 降序:建小堆
    1. 利用堆删除思想来进行排序
    • 逐次提取堆顶:将堆顶元素(最大值)与数组末尾交换,缩小堆范围,重新调整堆。
2. 详细步骤图解(以升序排序为例)

初始数组[4, 10, 3, 5, 1]

  1. 构建最大堆
    • 从最后一个非叶子节点(索引5//2 -1 =1)开始调整。
    • 调整后得到最大堆:[10, 5, 3, 4, 1]

💡 建堆的两种方式

  1. 向上调整建堆法: 时间复杂度O(nlogn),效率低,不推荐
  2. 向下调整建堆法 用向下调整建堆 时间复杂度O(n),推荐
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//建大堆
	//1.用向上调整建堆  时间复杂度O(nlogn),效率低,不推荐
	for (i = 0; i < n; i++) {
		AdjustUp(a, i);
	}
	//2.用向下调整建堆  时间复杂度O(n),推荐
	for (i = (n - 1 - 1)/2; i >= 0; i--) {
		AdjustDown(a, n, i);
	}

这里调整算法不再赘述。如有疑问,请学习“堆的实现” 或者与我交流哦😀

  1. 交换与调整
    • 交换堆顶10与末尾1 → 数组变为[1,5,3,4,10],有效堆范围减1。
    • 重新调整剩余元素[1,5,3,4]为最大堆 → [5,4,3,1]
    • 重复上述步骤,最终得到有序数组[1,3,4,5,10]
3. 代码实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//升序排序,用大根堆;
//降序排序,用小根堆。
void HeapSort(HPDataType* a,int n)
{
	//排升序
	int i = 0;

	//建大堆
	//1.用向上调整建堆  时间复杂度O(nlogn),效率低,不推荐
	/*for (i = 0; i < n; i++) {
		AdjustUp(a, i);
	}*/
	//2.用向下调整建堆  时间复杂度O(n),推荐
	for (i = (n - 1 - 1)/2; i >= 0; i--) {
		AdjustDown(a, n, i);
	}

	//排序
	int end = n - 1;
	while (end > 0) {
		swap(a, &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
	for (i = 0; i < n; i++) {
		printf("%d ", a[i]);
	}
}
4. 时间复杂度与特点
  • 时间复杂度:O(n log n)(构建堆O(n) + 每次调整O(log n))。
  • 原地排序:无需额外空间,适合内存敏感场景。
  • 不稳定性:相同元素可能交换顺序。

三、TopK问题:海量数据中的高效筛选
1. 问题场景
  • 需求:从一亿个数中快速找到前100大的数。
  • 挑战:直接排序时间复杂度O(n log n),内存可能无法容纳全部数据。
2. 堆的解决方案
  • 最小堆策略(找最大的K个元素):
    1. 初始化:用前K个元素构建一个最小堆(堆顶为当前最小值)。
    2. 遍历剩余元素:若当前元素 > 堆顶,则替换堆顶并调整堆。
    3. 最终结果:堆中保留的K个元素即为TopK。
  • 为什么用最小堆?
    • 堆顶是K个元素中的最小值,遇到更大的值时替换,确保堆中始终保留更大的候选。
3.如何创建这么多数?
  1. 随机数生成:利用rand生成随机数
  2. 写入文件
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void CreateNumber()
{
	int n = 10000;
	//时间种子
	srand((unsigned int)time(0));
	const char* file = "data.text";
	FILE* fp = fopen(file, "w");
	if (fp == NULL) {
		perror("fopen fail");
		return;
	}
	//写入文件
	for (size_t i = 0; i < n; ++i)
	{
		int x = rand() % 10000;
		fprintf(fp, "%d\n", x);
	}
	fclose(fp);
	fp = NULL;
}
3. 代码实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void PrintTopK(const char* file, int k)
{
	int* topk = (int*)malloc(sizeof(int) * k);
	if (topk == NULL) {
		perror("fopen fail");
		return;
	}
	FILE* fp = fopen(file, "r");
	if (fp == NULL) {
		perror("fopen fail");
		return;
	}
	//建前k个数的小根堆
	for (int i = 0; i < k; i++) {
		fscanf(fp,"%d",&topk[i]);
	}
	for (int i = (k - 2) / 2; i >= 0; i--) {
		AdjustDown(topk, k, i);
	}
	//遍历后面的数,若比堆top大,就覆盖并向下调整
	int val = 0;
	int ret = fscanf(fp, "%d", &val);
	while (ret != EOF) {
		if (val > topk[0]) {
			topk[0] = val;
			AdjustDown(topk, k, 0);
		}
		ret = fscanf(fp, "%d", &val);
	}
	for (int i = 0; i < k; i++) {
		printf("%d ", topk[i]);
	}
	free(topk);
	fclose(fp);
	fp = NULL;
}
4. 时间复杂度与优化
  • 时间复杂度:O(n log K),远优于O(n log n)。
  • 处理数据流:逐个读取数据,内存仅需维护大小为K的堆。

四、对比与总结

应用场景

核心思路

时间复杂度

空间复杂度

优势

堆排序

构建堆 + 交换堆顶

O(n log n)

O(1)

原地排序,适合内存敏感

TopK问题

维护大小为K的最小堆

O(n log K)

O(K)

高效处理海量数据或数据流


五、实际应用拓展
  1. 优先队列:操作系统任务调度、医院急诊分诊。
  2. 实时排行榜:游戏积分实时更新前100名。
  3. 合并K个有序链表:利用堆高效选择最小节点。

六、常见问题解答

Q1:为什么堆排序不如快速排序常用?

  • 堆排序的常数因子较大,且不稳定,实际运行速度可能慢于快速排序。

Q2:TopK问题能否用最大堆解决?

  • 可以,但需维护大小为n-K的堆,空间复杂度更高,适合K接近n的场景。

七、结语

堆结构凭借其高效的插入删除和极致的空间利用率,在排序与筛选问题中占据独特地位。掌握堆排序与TopK的解法,能显著提升处理大规模数据的能力。理解原理后,可尝试手写堆实现或结合具体业务场景优化代码,进一步巩固知识。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-03-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【数据结构与算法】堆的应用:堆排序和topk问题
aosei
2024/01/23
1160
【数据结构与算法】堆的应用:堆排序和topk问题
【数据结构】堆的实现和堆排序--TOP-K问题
堆是一种特殊的树形数据结构,常用于实现优先队列和堆排序。它基于完全二叉树,通常用数组表示。主要操作包括插入(通过上滤维护堆性质)和删除(通常删除堆顶元素,通过下滤恢复堆性质)。
用户11375356
2024/11/22
1010
【数据结构】堆的实现和堆排序--TOP-K问题
【数据结构】堆/堆排序(含top-k问题)(调整方式)(简洁,含代码)
YY的秘密代码小屋
2024/01/22
2160
【数据结构】堆/堆排序(含top-k问题)(调整方式)(简洁,含代码)
堆排序+TOPK问题
开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情 @TOC
lovevivi
2022/12/02
3120
堆排序+TOPK问题
【数据结构】二叉树-堆(top-k问题,堆排序,时间复杂度)
TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。 比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。 对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下: 1.用数据集合中前K个元素来建堆
秦jh
2024/01/19
4230
【数据结构】二叉树-堆(top-k问题,堆排序,时间复杂度)
【数据结构与算法】利用堆结构高效解决TopK问题
倔强的石头
2024/12/06
1740
【数据结构与算法】利用堆结构高效解决TopK问题
数据结构与算法:堆排序和TOP-K问题
冒泡排序的时间复杂度为O(N2),空间复杂度为O(1);qsort排序的时间复杂度为 O(nlogn),空间复杂度为O(logn),而今天所讲到的堆排序在时间与空间复杂度上相比于前两种均有优势
用户11029103
2024/03/19
1930
数据结构与算法:堆排序和TOP-K问题
【算法与数据结构】堆排序&&TOP-K问题
TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。 TOP-K问题是数据挖掘和信息检索中的一个重要问题。
学习起来吧
2024/03/23
1590
【算法与数据结构】堆排序&&TOP-K问题
堆与堆排序操作详解
堆(Heap)是计算机科学中一类特殊的数据结构,是最高效的优先级队列。堆通常是一个可以被看作一棵完全二叉树的数组对象。
用户11029129
2024/06/04
1460
堆与堆排序操作详解
【数据结构初阶第十五节】堆的应用(堆排序 + Top-K问题)
对于本节我们要提前掌握前一节课堆的相关实现才能学好本次的知识,一定要多画图多敲代码看看实现的效果是啥(Crazy!)开始吧!
云边有个稻草人
2025/02/23
810
【数据结构初阶第十五节】堆的应用(堆排序 + Top-K问题)
数据结构从入门到精通——堆
堆是一种特殊的树形数据结构,具有完全二叉树的特性。在堆中,父节点的值总是大于或等于(大顶堆)或小于或等于(小顶堆)其子节点的值。堆通常用于实现优先队列,其中每个元素都有一个优先级,优先级最高的元素总是位于堆的根节点。堆的插入和删除操作的时间复杂度都是O(log n),因此堆是一种高效的数据结构。此外,堆还可以用于实现内存管理,例如垃圾回收和内存分配等。
鲜于言悠
2024/03/20
5090
数据结构从入门到精通——堆
【数据结构】二叉查找树和二叉堆
这里我们介绍一种特殊的二叉树:二叉查找树(binary search tree) 。光看名字就可以知道,这种二叉树的主要作用就是进行查找 操作。
薄荷冰
2024/01/22
2530
【数据结构】二叉查找树和二叉堆
向上调整建堆与向下调整建堆的时间复杂度 AND TopK问题
堆排序是一种优于冒泡排序的算法, 那么在进行堆排序之前, 我们需要先创建堆, 为什么说堆排序的是优于冒泡排序的呢? 那么这个建堆的时间复杂度是多少呢?
用户11317877
2024/10/16
1250
向上调整建堆与向下调整建堆的时间复杂度 AND TopK问题
堆的应用:堆排序和TOP-K问题
上次才讲完堆的相关问题:二叉树顺序结构与堆的概念及性质(c语言实现堆 那今天就接着来进行堆的主要两方面的应用:堆排序和TOP-K问题
是Nero哦
2024/01/18
1640
堆的应用:堆排序和TOP-K问题
【初阶数据结构】堆排序和TopK问题
那么我们可以把14默认为是一个符合前提的堆,然后从12往后不断向数组中插入元素,并不断向上调整,直至把整个数组元素全部插完,即完成堆的建立.
MicroFrank
2023/01/16
6410
数据结构之堆的结构与实现
我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。
用户10923276
2024/03/28
1230
数据结构之堆的结构与实现
[数据结构]二叉树的顺序存储结构
普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结 构存储。现实中我们通常把堆 ( 一种二叉树 ) 使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统 虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
IT编程爱好者
2023/04/12
4240
[数据结构]二叉树的顺序存储结构
【数据结构】二叉树顺序存储结构堆的应用以及解决TOP-K问题
前面我们学习了堆这个数据结构,这种数据结构是一种顺序结构存储的完全二叉树,现在我们来看一看堆的应用。
Crossoads
2024/10/21
1050
【数据结构】二叉树顺序存储结构堆的应用以及解决TOP-K问题
2024-1-26学习任务:堆实现算法和topK问题
本文的学习任务:关于堆的实现以及相关的基础操作,包括向上调整算法和向下调整算法,同时利用该算法解决常见的topk问题,之后再对两种算法的时间复杂度进行分析,加深理解。
对编程一片赤诚的小吴
2024/01/29
1340
2024-1-26学习任务:堆实现算法和topK问题
数据结构——堆
调整算法用于堆的插入和删除后恢复堆的属性,但是用调整算法的前提是堆原本是有序的(大根堆或者小根堆)
HZzzzzLu
2024/11/26
1370
数据结构——堆
相关推荐
【数据结构与算法】堆的应用:堆排序和topk问题
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验