堆排序之“TOP-K”问题

这篇具有很好参考价值的文章主要介绍了堆排序之“TOP-K”问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、什么是TOP-K问题

二、解决思路 

一般的正常思路:

最优的解决思路:

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

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

向下调整算法

找出最大的K个数

完整版代码:


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

一、什么是TOP-K问题

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

二、解决思路 

一般的正常思路:

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

堆排序之“TOP-K”问题,数据结构,数据结构,c语言

一个整型变量需要四个字节空间,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函数关闭文件。

如果建小堆的方法—向下调整忘记了,我们来复习一下·

向下调整算法

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;
        }
    }
}
  • 通过传入参数获取到当前的左子节点的位置。
  • 当child位置小于数组元素个数时进行判断。
  • 进入循环,首先判断检查右子节点是否存在并且比左子节点的值小,如果是,将 child 更新为右子节点的索引,以确保选择更小的子节点进行比较。
  • 比较选定的子节点的值与父节点的值,如果子节点的值小于父节点的值,就交换它们。
  • 更新parent为新的子节点位置,更新child为新的左子节点位置,然后继续比较和交换,直到不再需要交换为止。
  • 如果当前子节点不小于当前父节点则停止循环。

 找出最大的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;
}

然后在文件中对五个数添加几位使其成为最大的五个数。堆排序之“TOP-K”问题,数据结构,数据结构,c语言

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

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

输出结果为: 堆排序之“TOP-K”问题,数据结构,数据结构,c语言文章来源地址https://www.toymoban.com/news/detail-736738.html

完整版代码:

#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;
}

到了这里,关于堆排序之“TOP-K”问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构(C语言实现)——二叉树的概念及二叉树顺序结构和链式结构的实现(堆排序+TOP-K问题+链式二叉树相关操作)

    前面学习了数据结构中线性结构的几种结构,顺序表,链表,栈和队列等,今天我们来学习一种非线性的数据结构——树。由于二叉树是数据结构中的一个重点和难点,所以本文着重介绍二叉树的相关概念和性质,以及二叉树的应用。 树是一种非线性的数据结构,它是由n(

    2023年04月21日
    浏览(45)
  • 【数据结构】堆(Heap):堆的实现、堆排序、TOP-K问题

    目录 堆的概念及结构 ​编辑 堆的实现  实现堆的接口 堆的初始化 堆的打印 堆的销毁 获取最顶的根数据  交换 堆的插入(插入最后) 向上调整(这次用的是小堆) 堆的删除(删除根) 向下调整(这次用的小堆) 堆排序 TOP-K问题 如果有一个关键码的集合 K = { , , , …

    2024年02月05日
    浏览(54)
  • 数据结构-堆的实现及应用(堆排序和TOP-K问题)

    1.堆的知识点: 下面我们通过一张图片来更加深刻地理解堆 上面我们说过,堆的物理结构是一个数组,逻辑结构是一个完全二叉树,所以堆的实际结构类似于顺序表,只不过我们的处理方式类似于二叉树 那么我们就可以用顺序表那样的结构来表示堆了 于是我们可以写出这样的代码

    2024年02月07日
    浏览(53)
  • 【数据结构】二叉树-堆(top-k问题,堆排序,时间复杂度)

     🌈个人主页: 秦jh__https://blog.csdn.net/qinjh_?spm=1010.2135.3001.5343 🔥 系列专栏: 《数据结构》https://blog.csdn.net/qinjh_/category_12536791.html?spm=1001.2014.3001.5482 ​​ 目录 堆排序 第一种  ​编辑 第二种  TOP-K问题 建堆的时间复杂度 向下调整建堆的时间复杂度:  向上调整建堆的时间

    2024年01月21日
    浏览(61)
  • 数据结构进阶篇 之 【堆的应用】(堆排序,TOP-K问题)详细讲解

    所有人都关心我飞的高不高,只有我妈关心我翅膀硬不硬 1.1 建堆 1.2 利用堆删除思想来进行排序 –❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀-正文开始-❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀– 学习一个知识,我们起码要直

    2024年04月10日
    浏览(47)
  • 『初阶数据结构 • C语言』⑭ - C语言实现用堆解决 TOP-K 问题

    目录 TopK函数实现 如何测试 完整源码  生活中我们经常能见到TopK问题,例如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。 所以, TopK问题即求出一组数据中前K个最大或最小的元素 ,一般情况下,数据量都比较大。 对于TopK问题,我们首先想到的可能是排序

    2024年02月16日
    浏览(44)
  • [数据结构 -- C语言] 堆实现Top-K问题,原来王者荣耀的排名是这样实现的,又涨知识了

    目录 1、什么是Top-K问题? 1.1 Top-K基本思路 2、Top-K问题逻辑分析 2.1 建堆,大小为K的小堆 2.2 将剩余的N - K 个元素依次与堆顶元素比较,大于就替换 2.3 打印堆 3、TopK实现代码 4、Top-K问题完整代码 结果展示: TopK问题的引入: 大家在玩王者荣耀的时候都遇到过xxx市第xxx某英雄

    2024年02月09日
    浏览(61)
  • 数据结构 | TOP-K问题

    TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。 就是从N个数里面找最大前K个(N远大于K) 思路一: N个数插入到堆里面,PopK次 时间复杂度是 O(N*logN) + K*logN == O(N*logN) N很大很大,假设N是100亿,K是10 100亿个整数大概需要40G左右 所以

    2024年02月05日
    浏览(41)
  • 数据结构--堆的实现-大根堆/小根堆/堆排序/堆排序稳定性证明/TOP-K

            前言          逆水行舟,不进则退!!!                目录        认识堆        堆的创建         1,向下调整的方法建立堆         2,以向下调整的方式建立小根堆         3,向上调整的方式建堆        堆的插入        堆的删除              

    2024年02月04日
    浏览(56)
  • 【数据结构】堆的应用+TOP-K问题+二叉树遍历

    欢迎来到我的: 世界 希望作者的文章对你有所帮助,有不足的地方还请指正,大家一起学习交流 ! 该篇文章写到主要是:堆排序、 TOP-K问题、二叉树链式结构的实现、二叉树的遍历等等;如果有朋友还不太了解堆以及二叉树可以翻看我的上一篇博客:堆和二叉树的概念; 最

    2024年02月07日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包