【手撕数据结构】Topk问题

目录

题目

TOP-K问题:即求数据结合中前K个最⼤的元素或者最⼩的元素,⼀般情况下数据量都⽐较⼤。

⽐如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。

对于Top-K问题,能想到的最简单直接的⽅式就是排序,但是:如果数据量⾮常⼤,排序就不太可取了

(可能数据都不能⼀下⼦全部加载到内存中)。最佳的⽅式就是⽤堆来解决,基本思路如下:

思路

1)⽤数据集合中前K个元素来建堆

前k个最⼤的元素,则建⼩堆

前k个最⼩的元素,则建⼤堆

2)⽤剩余的N-K个元素依次与堆顶元素来⽐较,不满⾜则替换堆顶元素

将剩余N-K个元素依次与堆顶元素⽐完之后,然后向下调整,堆中剩余的K个元素就是所求的前K个最⼩或者最⼤的元素

  • 有人说为什么建小堆,建大堆,直接把元素和堆顶交换就行了吗,这样堆顶一定是最大的。
  • 那么万一交换的是第二个大的数据呢?他们就直接出堆了?所以我们建小堆,通过向下调整,保证前k个数据一定在堆里面。

代码

cpp 复制代码
/*TopK*/
void HeapTopK()
{
	//1.使用一个文件指针指向这个文件
	FILE* fout = fopen("data.txt", "r");
	if (fout == NULL)
	{
		perror("fopen fail");
		return;
	}

	//2.读出前k个数放入数组中
	int k = 5;
	int max[5];
	for (int i = 0; i < k; ++i)
	{
		fscanf(fout, "%d", &max[i]);
	}

	//3.建立k个堆
	for (int i = ((k - 1) - 1) / 2; i >= 0; --i)
	{
		Adjust_Down(max, k, i);
	}

	//4.继续读取剩下的数据
	/*
	* 不断和堆顶数据做比较,比堆顶大就入堆,然后继续向下调整
	*/
	int val = 0;
	while ((fscanf(fout, "%d", &val)) != EOF)		//不停读取剩下的数据
	{
		if (val > max[0])
		{
			max[0] = val;		//替换为堆顶
			Adjust_Down(max, k, 0);		//将其做向下调整
		}
	}

	//5.打印数组中的数据,观看TopK个最大的数
	for (int i = 0; i < k; ++i)
	{
		printf("%d ", max[i]);
	}
	printf("\n");
	fclose(fout);
}
  • 可以看到,我这里使用的是一个随机值的写入,这一块也是我们在C语言中讲到过的,要使用到rand()和srand(),过程很简单,当然这里的【n】和【k】是由我自己来输入,因此下面的数组我们要设置成动态开辟。代码如下
cpp 复制代码
	int n, k;
	puts("请输入n和k的值:");
	scanf("%d%d", &n, &k);
	srand((unsigned int)time(NULL));		//随机种子

	FILE* fin = fopen("data2.txt", "w");		//若有,则打开写入;若无,则创建写入
	
	int randVal = 0;
	for (int i = 0; i < n; ++i)
	{
		randVal = rand() % 1000000;		//随机生成数字
		fprintf(fin, "%d\n", randVal);		//将每次随机生成的数字写入文件中
	}
	fclose(fin);

	///
	// 获取文件中前TopK个值
	//1.使用一个文件指针指向这个文件
	FILE* fout = fopen("data2.txt", "r");
	if (fout == NULL)
	{
		perror("fopen fail");
		return;
	}

	//2.读出前k个数放入数组中
	int* max = (int*)malloc(sizeof(int) * k);

	for (int i = 0; i < k; ++i)
	{
		fscanf(fout, "%d", &max[i]);		//此处无需加\n,因为读取时空格和回车自动作为分隔
	}

	//3.建立k个堆
	for (int i = ((k - 1) - 1) / 2; i >= 0; --i)
	{
		Adjust_Down(max, k, i);
	}

	//4.继续读取剩下的数据
	/*
	* 不断和堆顶数据做比较,比堆顶大就入堆,然后继续向下调整
	*/
	int val = 0;
	while ((fscanf(fout, "%d", &val)) != EOF)		//不停读取剩下的数据
	{
		if (val > max[0])
		{
			max[0] = val;		//替换为堆顶
			Adjust_Down(max, k, 0);		//将其做向下调整
		}
	}

	//5.打印数组中的数据,观看TopK个最大的数
	for (int i = 0; i < k; ++i)
	{
		printf("%d ", max[i]);
	}
	printf("\n");
	fclose(fout);
相关推荐
努力学习编程的伍大侠9 分钟前
基础排序算法
数据结构·c++·算法
XiaoLeisj37 分钟前
【递归,搜索与回溯算法 & 综合练习】深入理解暴搜决策树:递归,搜索与回溯算法综合小专题(二)
数据结构·算法·leetcode·决策树·深度优先·剪枝
Jackey_Song_Odd2 小时前
C语言 单向链表反转问题
c语言·数据结构·算法·链表
乐之者v2 小时前
leetCode43.字符串相乘
java·数据结构·算法
A懿轩A3 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
️南城丶北离4 小时前
[数据结构]图——C++描述
数据结构··最小生成树·最短路径·aov网络·aoe网络
✿ ༺ ོIT技术༻4 小时前
C++11:新特性&右值引用&移动语义
linux·数据结构·c++
拭心11 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
菜鸡中的奋斗鸡→挣扎鸡11 小时前
滑动窗口 + 算法复习
数据结构·算法
axxy200012 小时前
leetcode之hot100---240搜索二维矩阵II(C++)
数据结构·算法