【数据结构】堆的TopK问题

大家好,我是苏貝,本篇博客带大家了解堆的TopK问题,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
在这里插入图片描述


目录

  • 一. 前言
  • 二. TopK
  • 三. 代码

一. 前言

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决


二. TopK

思路:

  1. 用数据集合中前K个元素来建堆
    前k个最大的元素,则建小堆
    前k个最小的元素,则建大堆
  2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

下面我们用找出1000000个元素中最大的5个值举例

1
为1000000个元素赋值

1000000个元素当然不可能是由我们手动赋值,我们想到有srand函数搭配time函数可以生成随机值。
以写的形式打开文件"data.txt",如果文件不存在,就会建立该文件。
我们知道,srand(time(0))能生成的随机值只有3万多个,这就意味着如果我们只用随机数赋值的话,会有将近997万的数据是重复的,所以我们在随机数的基础上再加一个会变的数,这样重复的数字就会比较少。
最后,将随机数写入文件中。

void CreateData()
{
	FILE* fin = fopen("data.txt", "w");
	if (fin == NULL)
	{
		perror("fopen fail");
		return;
	}
	int n = 1000000;
	srand(time(0));
	for (int i = 0; i < n; i++)
	{
		int x = 0;
		x = (rand() + i) % 1000000;
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}

在上面代码的操作下,我们能保证所有的随机值都<1000000,那我们如何确保最后的结果是正确的呢?我们可以在执行完这个函数后,再打开文件,随意修改5个值,让它们都>1000000,这样最后的值只能是我们修改了的。

注意:
在修改完5个值后,将调用main函数中调用CreateData函数的代码注释掉,否则再次运行程序,文件里的数据会重新被修改
在这里插入图片描述

2
如何建小堆?

1.先定义一个指针指向k个元素的数组
2.将文件的前k个元素边插入,边向上调整,最后得到小堆

了解fscanf

//void swap(int* a, int* b)
//{
//	int tmp = *a;
//	*a = *b;
//	*b = tmp;
//}


//void AdjustUp(int* a, int child)
//{
//	assert(a);
//
//	int parent = (child - 1) / 2;
//	while (child > 0)
//	{
//		if (a[child] < a[parent])
//		{
//			swap(&a[child], &a[parent]);
//			child = parent;
//			parent = (child - 1) / 2;
//		}
//		else
//			break;
//	}
//}
    FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen fail");
		return;
	}
    //1.建有k个元素的小堆
	int* minheap = (int*)malloc(sizeof(int) * k);
	if (minheap == NULL)
	{
		perror("fopen fail");
		return;
	}

	//2.将前k个元素插入小堆中
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &minheap[i]);
		AdjustUp(minheap, i);
	}

3
要找出最大的k个值时,为什么不用大堆?

因为如果最大值先出来,就占据了堆顶的位置,此时次大值就因为<最大值而不能进入大堆中。

4
得到TopK

用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素,再将新的堆顶元素向下调整

//void AdjustDown(int* a, int size, int parent)
//{
//	assert(a);
//
//	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;
//		}
//	}
//
//}

    //3.遍历,如果有数比堆顶元素大的话,让堆顶元素=该元素,再向下调整
	while (fscanf(fout, "r") != EOF)
	{
		int x = 0;
		fscanf(fout, "%d", &x);
		if (x > minheap[0])
		{
			minheap[0] = x;
			AdjustDown(minheap, k, 0);
		}
	}

三. 代码

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


//构建数据
void CreateData()
{
	FILE* fin = fopen("data.txt", "w");
	if (fin == NULL)
	{
		perror("fopen fail");
		return;
	}
	int n = 1000000;
	srand(time(0));
	for (int i = 0; i < n; i++)
	{
		int x = 0;
		x = (rand() + i) % 1000000;
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}


void swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}


void AdjustUp(int* a, int child)
{
	assert(a);

	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			break;
	}
}


void AdjustDown(int* a, int size, int parent)
{
	assert(a);

	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 PrintTopK(FILE* file, int k)
{
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen fail");
		return;
	}

	//1.建有k个元素的小堆
	int* minheap = (int*)malloc(sizeof(int) * k);
	if (minheap == NULL)
	{
		perror("fopen fail");
		return;
	}

	//2.将前k个元素插入小堆中
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &minheap[i]);
		AdjustUp(minheap, i);
	}

	//3.遍历,如果有数比堆顶元素大的话,让堆顶元素=该元素,再向下调整
	while (fscanf(fout, "r") != EOF)
	{
		int x = 0;
		fscanf(fout, "%d", &x);
		if (x > minheap[0])
		{
			minheap[0] = x;
			AdjustDown(minheap, k, 0);
		}
	}

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

	free(minheap);
	fclose(fout);
}

//TopK 找出最大的k个值
int main()
{
	//CreateData();
	PrintTopK("data.txt", 5);
	return 0;
}

在这里插入图片描述


好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙Harmony应用开发—ArkTS声明式开发(通用属性:背景设置)

设置组件的背景样式。 说明&#xff1a; 从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 background10 background(builder: CustomBuilder, options?: { align?: Alignment }) 设置组件背景。 系统能力&#xff1a; …

外包干了2年,技术退步明显

先说一下自己的情况&#xff0c;研究生&#xff0c;19年进入广州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&#xf…

CCF CSP 202006-4 1246 (100分详细题解,矩阵乘法+快速幂)

202006-4 1246 &#xff08;100分详细题解&#xff0c;矩阵乘法快速幂&#xff09; 可以先看下csp官方的思路讲解&#xff0c;大思路是状态转移&#xff0c;先看下面s<2的情况 1 -> 2 2 -> 4 4 -> 16 6 -> 6 64 4 16 -> 26&#xff08;不考虑2&#xff0c;6…

“计算机界三大神书”之一 ——SICP【赠书活动】

目录 前言一、SICP简介二、改编本SCIP JS福利总结 前言 《计算机程序的构造和解释》&#xff08;Structure and Interpretation of Computer Programs&#xff0c;简记为SICP&#xff09;是MIT的基础课教材&#xff0c;出版后引起计算机教育界的广泛关注&#xff0c;对推动全世…

Java开发必须掌握,java高级工程师面试类的加载

前言 这些算法&#xff0c;都是小编一点一点看的大佬们的方法&#xff0c;自己积累的. 如果有什么描述的不对 点击领取2024完整开源项目《一线大厂Java面试题解析后端开发学习笔记最新架构讲解视频实战项目源码讲义》 的地方还望大佬赐教 多交流才能进步&#xff0c;加油&#…

Apache POI 解析和处理Excel

摘要&#xff1a;由于开发需要批量导入Excel中的数据&#xff0c;使用了Apache POI库&#xff0c;记录下使用过程 1. 背景 Java 中操作 Excel 文件的库常用的有Apache POI 和阿里巴巴的 EasyExcel 。Apache POI 是一个功能比较全面的 Java 库&#xff0c;适合处理复杂的 Offi…

errno 和 strerror函数

今天写了一个很简单的代码&#xff0c;编译时没啥错误和警告&#xff08;主要编译选项没开启警告&#xff09;&#xff0c;然后运行时居然 segmentation fault&#xff0c;把我给看傻了&#xff0c;代码如下&#xff1a; #include <stdio.h> #include <stdlib.h> …

2024护网面试题精选(一)

0x00.基础漏洞篇 00-TOP10漏洞 1.SQL注入 2.失效的身份认证和会话管理 3.跨站脚本攻击XSS 4.直接引用不安全的对象 5.安全配置错误 6.敏感信息泄露 7.缺少功能级的访问控制 8.跨站请求伪造CSRF 9.实验含有已知漏洞的组件 10.未验证的重定向和转发 01-SQL注入漏洞 …

PackagesNotFoundError:学习利用报错信息找到解决方法

反思&#xff1a;之前看到报错经常是直接复制报错信息去网上搜&#xff0c;但很多情况下报错信息里其实就给出了解决方案 报错信息&#xff1a; Collecting package metadata (current_repodata.json): done Solving environment: unsuccessful initial attempt using frozen …

关于Mybatis-Plus报错 Not Found TableInfoCache 解决办法

0. 接口结构&#xff1a;1. 方法报错&#xff1a;2. 解决方法&#xff1a;3. 原因分析&#xff1a; 0. 接口结构&#xff1a; 【接口】&#xff1a; public interface PurchaseOrderService extends IService<PurchaseOrder> {}【接口实现类】&#xff1a; public cla…

某多多anti_token(先水个文后续会完善)第一部分

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01;wx a15018…

某酷ckey140逆向(之前下架了重新上传补发)

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01;wx a15018…

[R] Underline your idea with ggplot2

Preview: # 介绍&#xff1a;之前的教程中&#xff0c;我们学习了如何使条形图或直方图看起来更好 比如&#xff1a; 1. How to select a graph calibrate the geom part 2. How to select variables calibrate the aes part 3. How to add a title calibrate the labs …

【Mybatis】批量映射优化 分页插件PageHelper 逆向工程插件MybatisX

文章目录 一、Mapper批量映射优化二、插件和分页插件PageHelper2.1 插件机制和PageHelper插件介绍2.2 PageHelper插件使用 三、逆向工程和MybatisX插件3.1 ORM思维介绍3.2 逆向工程3.3 逆向工程插件MyBatisX使用 总结 一、Mapper批量映射优化 需求: Mapper 配置文件很多时&…

16:00面试,16:08就出来了,问的问题过于变态了。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到2月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

Sparse A*算法的时间复杂度

Sparse A*(SAS)算法是A*算法的变型算法&#xff0c;下面将结合A*算法的流程分析SAS的时间复杂度。对于SAS算法而言&#xff0c;其航迹规划的时间 T T T主要由两部分组成&#xff1a; T s T_s Ts​&#xff1a;在当前结点扩展可行子结点的时间&#xff1b; T 0 T_0 T0​&#…

Qt QPainter的使用方法

重点&#xff1a; 1.QPainter在QWidget窗口的paintEvent中使用。 2.QPainter通常涉及到设置画笔、设置画刷、绘图&#xff08;QPen、QBrush、drawxx&#xff09;三个流程。 class Widget : public QWidget {Q_OBJECTprotected:void paintEvent(QPaintEvent *event) Q_DEC…

基于51单片机的四位并行数据主从机传输设计

基于51单片机的四位并行数据主从机传输设计[proteus仿真] 主从机通信系统这个题目算是课程设计和毕业设计中常见的题目了&#xff0c;本期是一个基于51单片机的四位并行数据主从机传输设计 需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】&#xff0c;赞赏任意文…

机器学习---数据分割

之前的文章中写过&#xff0c;我们可以通过实验测试来对学习器的泛化误差进行评估并进而做出选择。 为此&#xff0c;需使用一个“测试集"(testing set)来测试学习器对新样本的判别能力&#xff0c;然后以测试集上的“测 试误差”(testing error)作为泛化误差的近似。通…

操作系统系列学习——内核级线程实现

文章目录 前言内核级线程实现 前言 一个本硕双非的小菜鸡&#xff0c;备战24年秋招&#xff0c;计划学习操作系统并完成6.0S81&#xff0c;加油&#xff01; 本文总结自B站【哈工大】操作系统 李治军&#xff08;全32讲&#xff09; 老师课程讲的非常好&#xff0c;感谢 【哈工…