堆排序之——TopK问题

思维导图:

 

一,TopK算法的运用 

   TopK的算法在我们的日常生活中可谓是大有用处,比如你在点外卖时外卖榜单上的销量前几名的筛选,富豪排行榜的榜单人物的筛选,游戏排位……等等领域都会有TopK算法的涉及。TopK问题的用处可大了!

二,TopK算法的实现

   现在我先抛出一个问题。比如我要在一百万个数据里面寻找前五大的数据,那我一个小白会怎么做呢?毫无疑问,我会选择暴力求解!

   思路:

   1.将这一百万个数据排成大堆,然后将这个大堆删除k次那我每次在根节点得到的数据就是最大的前k个数。

   这个想法非常美好,其实时间复杂度也不是很高!但是这个算法的空间复杂度却非常的高。一百万个数据就要建立一个400万个字节大小的堆。而且堆是不能在磁盘建立的只能在内存中建立,所以你的那点小小的内存会被占满!!!正是因为这个原因,这个方法就不能满足我们的需求!

2.TopK算法求解:建立一个只有k个数据的小堆,然后将这个堆的堆顶元素与剩下的N-k个数据进行比较。如果有数据大于堆顶元素那就代替堆顶元素进堆,然后通过向下调整算法将这个新的堆再次变成小堆!再与剩下的元素进行比较重复上面的操作!这样,这个TopK算法就可以解决上面算法空间复杂度太高的问题!

算法实现:

第一步:实现一个向下调整算法建堆

这个操作是为了建一个小堆。

这个代码要注意的点:

   1.父节点与子节点的交换限制条件是子节点要在数组的范围内

即:n是数组的长度

while (child < n)

2.我们默认的要与父节点交换的节点是左节点,即:

int child = 2 * parent + 1;

但是左节点不一定比右节点小,所以在右节点小于左节点时,并且在右节点存在的情况下我们要将让比较小的右节点来与父节点进行交换,即:

if (child + 1 < n && a[child + 1] < a[child])
		{
			child++;
		}

代码:

void swap(int* p1, int* p2)//交换函数
{
	int temp = *p1;
	*p1 = *p2;
	*p2 = *p1;
}
Adjustdown(int* a, int n, int parent)//向下调整算法
{
	int child = 2 * parent + 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 = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}

 第二步,实现TopK算法并将前k个数据进行打印:

在这一步骤中,我就是按照TopK算法的实现思路实现了这个算法。

这个算法的空间复杂度就是O(1),主要看创建堆的这一步:

int* KminHeap = (int*)malloc(sizeof(int) * k);//创建一个只有k个数据的小堆

时间复杂度是:O(n*logN).主要看这一步:

1.向下调整的时间复杂度是O(logN)。O(logN)*(n-k) = O(N*logN)

for (int i = k;i < n;i++)
	{
		if (a[i] > KminHeap[0])//如果后面的n-k个数据中有比KminHeap[0]大的数据就插入堆中
		{
			KminHeap[0] = a[i];
			Adjustdown(KminHeap, k, 0);//调整
		}
	}

可以说这个算法完美的解决了第一个思路空间复杂度太大的问题! 

代码:

void TopKPrint(int* a, int n, int k)
{
	int* KminHeap = (int*)malloc(sizeof(int) * k);//创建一个只有k个数据的小堆
	if (KminHeap == NULL)
	{
		perror("malloc fail");
		return;
	}

	for (int i = 0;i < k;i++)//将数组的前k个数据插入到KminHeap堆中
	{
		KminHeap[i] = a[i];
	}
	Adjustdown(KminHeap, k, 0);//向下调整使KminHeap变成小堆

	for (int i = k;i < n;i++)
	{
		if (a[i] > KminHeap[0])//如果后面的n-k个数据中有比KminHeap[0]大的数据就插入堆中
		{
			KminHeap[0] = a[i];
			Adjustdown(KminHeap, k, 0);//调整
		}
	}

	for (int i = 0;i < k;i++)//打印
	{
		printf("%d ", KminHeap[i]);
	}
	printf("\n");
}

第三步,创建数据

1. 我在创建数据时不想要一个固定的数据,所以用了一个可以随机生成数据的rand()函数:

a[i] = rand() % 100000;//生成随机数

这个代码理论上可以随机生成100000内的数据,但其实不行。它最多只能生成32767以内的数据(所以叫虚伪的随机数)。

2.要使用这个代码,那就需要一个生成随机数的种子:

srand(time(0));//生成随机数的种子

这个函数的参数是time(0),因为在计算机中只有时间time是一直在变化的!只有参数一直在变化rand()才能生成不同的数据。

代码:

int main()
{
	srand(time(0));//生成随机数的种子
	int n = 1000;
	int* a = (int*)malloc(sizeof(int) * n);
	if (a == NULL)
	{
		perror("malloc fail");
		return;
	}

	for (int i = 0;i < n;i++)
	{
		a[i] = rand() % 100000;//生成随机数
	}

	TopKPrint(a, n, 5);


	return 0;
}

写到这里我们的代码就写完了!

二, 更换数据读取位置

前面我们说,第一种排序的思路为什么不行来着?是不是因为它的空间复杂度太高了啊?还有一个限制就是因为我们只能在内存中建堆。而堆在物理上其实就是数组。那现在我在干什么?

	int* a = (int*)malloc(sizeof(int) * n);

我是不是也在建立一个4*n字节大小的堆啊?当我的数据的数量很大时我是不是要完蛋啊。

很明显是的!所以我们要换一种读取数据的方式,我们要将读取的数据放到磁盘中。所以我在这里搞得第二种读取数据的方式就是从文件中读取数据。

2.1创建文件

创建文件的数据时仍然是像之前一样生成随机值。然后执行文件操作生成文件。

代码:

void CreatData(void)
{
	int n = 10000;
	srand(time(0));

	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen fail");
		return;
	}
	for (int i = 0;i < n;i++)
	{
		int x = rand() % 100000;
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}

2.2TopKPrint实现

思路与之前使用数组实现TopK算法基本一致。

代码

void TopKPrint(int k)
{
	const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen fail");
		return;
	}

	int* kminHeap = (int*)malloc(sizeof(int) * k);
	if (kminHeap == NULL)
	{
		perror("malloc fail");
		return;
	}

	for (int i = 0;i < k;i++)
	{
		fscanf(fout, "%d", &kminHeap[i]);
	}
	Adjustdown(kminHeap, k, 0);

	while (!feof(fout))
	{
		int val = 0;
		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]);
	}
}

 

 

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

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

相关文章

github创建仓库和拉取代码

目录 一、git创建仓库 第一步&#xff1a;首先登录github 第二步&#xff1a;进入建立的仓库(或者新建仓库) 第三步&#xff1a;创建成功 第四步&#xff1a;在本地新建一个文件夹&#xff0c;然后在文件夹下打开git bash 第五步&#xff1a;在git bash命令框执行git init…

专业解读财务共享实现财务数智化转型的有效路径

近年来&#xff0c;随着数字经济的飞速发展&#xff0c;各大企业全面开启数智化转型之路&#xff0c;作为企业数智化转型的重要内容&#xff0c;财务数智化转型始于财务共享服务。然而&#xff0c;财务共享建设并不是一蹴而就的&#xff0c;如何通过财务共享实现财务数智化转型…

什么是分布式软件系统

:什么是分布式软件系统&#xff1f;分布式软件系统是什么意思&#xff1f; 分布式软件系统(Distributed Software Systems)是支持分布式处理的软件系统,是在由通信网络互联的多处理机体系结构上执行任务的系统。它包括分布式操作系统、分布式程序设计语言及其编译(解释)系统、分…

阻抗板是否高可靠,华秋有话说

随着高频高速电子产品的快速发展&#xff0c;信号传输过程更容易出现反射、串扰等信号完整性问题&#xff0c;且频率越高、传输速率越快&#xff0c;信号损耗越严重&#xff0c;如何降低信号在传输过程中的损耗、保证信号完整性是高频高速PCB发展中的巨大挑战。 在高速PCB设计…

Spring源码(一) — 序言

序言 Java程序员的日常开发一定都离不开Spring的框架&#xff0c;从Spring、SpringMVC、SpringBoot、SpringCloud… 而Spring框架就是Spring家族中最基础也是最重要的一个框架。 Spring 我们常说的Spring往往都绕不开IOC&#xff08;控制反转&#xff09;和AOP&#xff08;切…

【TellMeCode】使用VSCODE + ChatGPT辅助分析推测源码

【TellMeCode】使用VSCODE ChatGPT辅助分析推测源码 0x00 功能简介 根据代码上下文相关信息&#xff0c;如工作区文件夹名称&#xff0c;代码所在路径等一系列信息&#xff0c;提供给大模型更多元和尽可能多的信息&#xff0c;利用其自身优势去检索相关的文档和博客&#xf…

QT开发实战-动态壁纸软件

动态壁纸软件开发 项目源代码在下面链接获取: ----------------------------- 开发者:CodeSharkSJ 希望此项目能加强你对Qt的应用 文章目录 项目图与开发环境核心技术原理自定义窗口程序UI布局背景绘制样式表基本实现QWebEngineQMedia使用系统托盘隐藏记忆功能应用程序打包 …

RestCloud荣膺广东省优秀软件产品奖,引领国内数据集成领域!

近日&#xff0c;“2022年广东软件风云榜”名单公布&#xff0c;“谷云ETL数据交换软件”凭借其在助力企业数字化转型升级过程中的卓越表现&#xff0c;荣获由羊城晚报报业集团、广东软件行业协会、广东省大数据协会联合颁发的“优秀软件产品和解决方案”奖。 数字化转型是推动…

【P38】JMeter 随机控制器(Random Controller)

文章目录 一、随机控制器&#xff08;Random Controller&#xff09;参数说明二、测试计划设计2.1、测试计划一2.2、测试计划二2.3、勾选忽略子控制器块 一、随机控制器&#xff08;Random Controller&#xff09;参数说明 可以让控制器内部的逻辑随机执行一个&#xff0c;一般…

深度学习-第T8周——猫狗识别

深度学习-第T8周——猫狗识别 深度学习-第T8周——猫狗识别一、前言二、我的环境三、前期工作1、导入数据集2、查看图片数目 四、数据预处理1、 加载数据1.1、设置图片格式1.2、划分训练集1.3、划分验证集1.4、查看标签1.5、再次检查数据1.6、配置数据集 2、数据可视化 五、搭建…

机器学习常识 7: 决策树

摘要: 决策树是一种与人类思维一致, 可解释的模型. 1. 决策树的结构 人类的很多知识以决策规则的形式存储: 如果今天是阴天 (outlook overcast), 就去打球.如果今天出太阳 (outlook sunny) 而且湿度不高于 70% (humidity ≤ \le ≤ 70), 就去打球.如果今天出太阳 (outloo…

1688商品ID采集一件代发详情页面数据

本篇博文介绍了对1688商品详情API的二次封装&#xff0c;将URL参数封装成Python函数&#xff0c;直接传入参数即可获取搜索结果&#xff0c;例如1688商品标题、价格、一件代发、sku属性和URL等。提供了详细的代码示例和接口调用Demo。 1688.item_get-获得1688商品详情数据 1.请…

APP开发死亡潮来临 小程序是否会取而代之?

移动互联网的发展&#xff0c; APP开发行业也迎来了它的大时代。据有关数据显示&#xff0c;2017年上半年国内新增的 App数量达到了创纪录的449万款&#xff0c;用户使用时长超过了200亿分钟。移动互联网已成为名副其实的“流量”产业&#xff0c;也因此诞生出一大批 APP开发公…

Maven 概述及下载安装

一、为什么要学习 Maven 我们构建一个项目需要用到很多第三方的类库&#xff0c;就需要引入大量的jar包&#xff0c;并且Jar包之间的关系错综复杂&#xff0c;缺少任何一个Jar包都会导致项目编译失败。Maven 能帮助我们下载及管理依赖。 本地项目代码开发完成后&#xff0c;我…

类和对象【3】初始化列表

全文目录 引言初始化列表定义特性 总结 引言 上一篇文章中介绍了构造函数&#xff0c;它可以在实例化一个类对象的时候自动调用&#xff0c;以初始化类对象&#xff1a; 戳我看默认成员函数详解 但是&#xff0c;不难发现&#xff0c;在构造函数体中对成员变量的初始化其实是属…

gdb调试 与 coredump

gdb调试 与 coredump调试 1. 启动gdb2.gdb中的相关命令3. coredump调试&#xff08;附属于gdb调试中一种&#xff0c;当程序出现错误时&#xff0c;会使用coredump调试&#xff09;1&#xff09;coredump是什么&#xff1f;2&#xff09;前期设置3&#xff09;什么情况下会导致…

word打印为pdf去掉批注和修订记录

对于这个问题某乎上充斥着垃圾回答&#xff0c;大多引流到自家开发的pdf产品上。其实背后的方法都是一样的&#xff0c;就是关掉批注&#xff0c;用word自带的功能就能解决&#xff0c;凡是word编辑软件都有类似功能 直接用word打印为pdf后的效果 下图为打印出来的pdf文件&…

【算法】不使用LinkedHashMap实现一个LRU缓存

文章目录 什么是LRU&#xff1f;设计思路代码实现 LRU是我在面试过程中遇到的比较多的算法题了&#xff0c;并且我自己的项目中也手写了LRU算法&#xff0c;所以觉得还是有必要掌握一下这个重要的算法的。 什么是LRU&#xff1f; LRU是一种缓存淘汰策略。 我们知道&#xff0…

大环境不好难找工作?三面阿里,幸好做足了准备,已拿offer

大环境不好难找工作&#xff1f;三面阿里&#xff0c;幸好做足了准备&#xff0c;已拿offer 三面大概九十分钟&#xff0c;问的东西很全面&#xff0c;需要做充足准备&#xff0c;就是除了概念以外问的有点懵逼了&#xff08;呜呜呜&#xff09;。回来之后把这些题目做了一个分…

R-Meta分析与【文献计量分析、贝叶斯、机器学习等】多技术融合实践与拓展

Meta分析是针对某一科研问题&#xff0c;根据明确的搜索策略、选择筛选文献标准、采用严格的评价方法&#xff0c;对来源不同的研究成果进行收集、合并及定量统计分析的方法&#xff0c;最早出现于“循证医学”&#xff0c;现已广泛应用于农林生态&#xff0c;资源环境等方面。…