堆的应用——TOP-K问题

堆的应用——TOP-K问题

  • TOP-K 问题
  • 解决方法
    • 一、排序后选择
    • 二、简单数组维护
    • 三、使用堆优化简单数组方案
  • TOP-K 问题实例的堆代码参考(环境为VS2022的C语言)
    • 生成 1 千万个整数
    • 生成后检查文件
    • 建小堆处理问题
    • 验证正确性
    • 完整代码:

TOP-K 问题

即求相同数据中前K个最大的元素或者最小的元素。(一般情况下数据量都比较大)

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

解决方法

一、排序后选择

对于 Top-K 问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。
在这里插入图片描述
使用快速排序,时间复杂度为O(N * logN),空间复杂度为O(logN)。

二、简单数组维护

使用一个长为 K 的临时数组:

  1. 拷贝数据集合中前 K 个元素,遍历 N - K 个剩余元素;
  2. 若要求 K 个最大元素,寻找临时数组中最小的元素,对比数据集合元素,符合条件覆盖临时数组中最小元素;
  3. 若覆盖后,需要再次寻找临时数组中的最小元素与下一个数据集合对比。
    在这里插入图片描述

由于每次覆盖都需要重新寻找最小值(时间复杂度为 O(K) ),显然这个方法的时间复杂度为 O(N * K),空间复杂度为O(K)。在 K 接近 N 后时间复杂度退化到 O(N ^ 2),方法并不理想。

即便在拷贝 K 个数据元素对临时数组排序,之后使用插入的方式优化,在最坏的情况下时间复杂度依然是 O(N ^ 2)。

三、使用堆优化简单数组方案

最佳的方式就是用堆来解决,基本思路如下:

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

将剩余 N - K 个元素依次与堆顶元素比完之后,堆中剩余的 K 个元素就是所求的前 K 个最小或者最大的元素。

二叉树顺序结构——堆的结构与实现这篇文章有对于堆的详细讲解,这里不赘述。

在这里插入图片描述
可以明显观察到,让数据下沉消耗时间复杂度为 O (logK),则整体时间复杂度为 O(N * logK),空间复杂度为 O(K)。即便 K 接近 N 时,O (N * logN) 的复杂度也与排序相当,不过此时空间复杂度为 O (N)。

TOP-K 问题实例的堆代码参考(环境为VS2022的C语言)

这里我们来用实际数据试试堆的优势。

生成 1 千万个整数

假设 需要在 1 千万个无序整数的文件中打印 10 个最大的数,我们先生成 1 千万个整数存入 data.txt 文本文件,然后让堆来获取,最后打印。

#define _CRT_SECURE_NO_WARNINGS 1

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

void fileNumberCreate()
{
	FILE* fin = fopen("data.txt", "w");
	if (fin == NULL)
	{
		perror("open file failed");
		fclose(fin);
		return;
	}

	// 一千万个整数
	int count = 10000000;
	while (count)
	{
		int num = rand() % 10000 + rand() % 10000 * 10000;
		// 整数范围 0 ~ 99999999
		fprintf(fin, "%d ", num);

		--count;
	}

	fclose(fin);
}

int main()
{
	srand((unsigned int)time(NULL));
	fileNumberCreate();

	return 0;
}

生成后检查文件

在这里插入图片描述

建小堆处理问题

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

void AdjustDown(int* arr, int n, int parent)
{
	// 先找到左孩子
	int child = parent * 2 + 1;
	while (child < n)						// 当child 超过范围退出
	{
		// 假设法
		if (child + 1 < n && arr[child] > arr[child + 1])
		{
			++child;
		}

		// 若不符合堆的性质,则调整,反之退出
		if (arr[parent] > arr[child])
		{
			swap(&arr[parent], &arr[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;							// 满足堆的性质,直接退出
		}
	}
}

void top_k()
{
	FILE* fout = fopen("data.txt", "r");
	if (fout == NULL)
	{
		perror("open file failed");
		fclose(fout);
		return;
	}

	int k = 10;

	int* arr = (int*)malloc(sizeof(int) * k);

	// 先读入10个数据
	for (int i = 0; i < k; ++i)
	{
		fscanf(fout, "%d", &arr[i]);
	}

	// 建小堆
	for (int i = k / 2 - 1; i >= 0; --i)
	{
		AdjustDown(arr, k, i);
	}

	// 开始遍历
	int n = 0;
	while (fscanf(fout, "%d", &n) != EOF)
	{
		if (n > arr[0])
		{
			arr[0] = n;
			AdjustDown(arr, k, 0);
		}
	}

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

	fclose(fout);
}

int main()
{
	srand((unsigned int)time(NULL));
	//fileNumberCreate();
	top_k();

	return 0;
}

验证正确性

如果想要测试是否正确,可手动将一些数据改成更大的整数。这里数的范围为 0 ~ 99999999,那我们在文本中添加10个1亿大小的数。
在这里插入图片描述
结果:
在这里插入图片描述

完整代码:

#define _CRT_SECURE_NO_WARNINGS 1

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

void fileNumberCreate()
{
	FILE* fin = fopen("data.txt", "w");
	if (fin == NULL)
	{
		perror("open file failed");
		fclose(fin);
		return;
	}

	// 一千万个整数
	int count = 10000000;
	while (count)
	{
		int num = rand() % 10000 + rand() % 10000 * 10000;

		fprintf(fin, "%d ", num);

		--count;
	}

	fclose(fin);
}

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

void AdjustDown(int* arr, int n, int parent)
{
	// 先找到左孩子
	int child = parent * 2 + 1;
	while (child < n)						// 当child 超过范围退出
	{
		// 假设法
		if (child + 1 < n && arr[child] > arr[child + 1])
		{
			++child;
		}

		// 若不符合堆的性质,则调整,反之退出
		if (arr[parent] > arr[child])
		{
			swap(&arr[parent], &arr[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;							// 满足堆的性质,直接退出
		}
	}
}

void top_k()
{
	FILE* fout = fopen("data.txt", "r");
	if (fout == NULL)
	{
		perror("open file failed");
		fclose(fout);
		return;
	}

	int k = 10;

	int* arr = (int*)malloc(sizeof(int) * k);

	// 先读入10个数据
	for (int i = 0; i < k; ++i)
	{
		fscanf(fout, "%d", &arr[i]);
	}

	// 建小堆
	for (int i = k / 2 - 1; i >= 0; --i)
	{
		AdjustDown(arr, k, i);
	}

	// 开始遍历
	int n = 0;
	while (fscanf(fout, "%d", &n) != EOF)
	{
		if (n > arr[0])
		{
			arr[0] = n;
			AdjustDown(arr, k, 0);
		}
	}

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

	fclose(fout);
}

int main()
{
	srand((unsigned int)time(NULL));
	//fileNumberCreate();
	top_k();

	return 0;
}

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

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

相关文章

【讯为Linux驱动开发】6.自旋锁spinlock

【自旋锁】 线程A获取自旋锁后&#xff0c;B假如想获取自旋锁则只能原地等待&#xff0c;仍占用CPU&#xff0c;不会休眠&#xff0c;直到获取自旋锁为止。 【函数】 DEFINE SINLOCK(spinlock t lock) 定义并初始化一个变量int spin lock init(spinlock t*lock) 初始化自…

设计通用灵活的LabVIEW自动测试系统

为了在不同客户案例中灵活使用不同设备&#xff08;如采集卡、Modbus模块&#xff09;且保持功能一致的LabVIEW自动测试系统&#xff0c;需要采用模块化的软件架构、配置文件管理、标准化接口和良好的升级维护策略。本文从软件架构、模块化设计、配置管理、升级维护、代码管理和…

docker-compose启动oracle11、并使用navicat进行连接

一、docker-compose.yml version: 3.9 services:oracle:image: registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11grestart: alwaysprivileged: truecontainer_name: oracle11gvolumes:- ./data:/u01/app/oracleports:- 1521:1521network_mode: "host"logging:d…

安卓/iOS/Linux系统影音边下边播P2P传输解决方案

在当今的数字时代&#xff0c;IPTV 影音行业正经历着快速的发展和变革&#xff0c;但影音行业的流量带宽成本一直很高&#xff0c;有没有什么办法既能保证现有的用户观看体验&#xff0c;又能很好降低流量带宽成本呢? P2P技术可能是一个很好的选择&#xff0c;它不仅仅可以提…

计算机组成原理(六)

0x12345678和12345678H都是指同一个十六进制,也就是12345678&#xff0c;不过是不同的编程语言的写法而已 具体来说&#xff0c;如果有 n 根地址线&#xff0c;计算机可以寻址的内存空间大小是 2^n 字节。 24根地址线&#xff1a; 如果一个系统有24根地址线&#xff0c;意味着它…

【日记】第一次养植物,没什么经验……(781 字)

正文 前两天梦见灵送的几盆植物全都死掉了。梦里好伤心。醒来与她说这件事&#xff0c;她宽慰我说&#xff0c;梦都是反着的&#xff0c;肯定能活得很好的。于是忽然记起昨天给植物换水时&#xff0c;文竹的根居然从花盆底部伸吊了出来&#xff0c;以前都没有这种情况来着&…

你知道古代青铜器的原色是什么吗?

在中国悠久的历史中&#xff0c;青铜器作为中华文明的瑰宝&#xff0c;一直以其独特的艺术魅力和深厚的文化内涵吸引着世人的目光。然而&#xff0c;对于大多数人来说&#xff0c;青铜器的形象往往与电视剧中的描绘有所出入。那些在剧中常见的青绿色青铜器&#xff0c;让许多观…

Kafka 负载均衡挑战及解决思路

本文转载自 Agoda Engineering&#xff0c;介绍了在实际应用中&#xff0c;如何应对 Kafka 负载均衡所遇到的各种挑战&#xff0c;并提出相应的解决思路。本文简要阐述了 Kafka 的并行性机制、常用的分区策略以及在实际操作中遇到的异构硬件、不均匀工作负载等问题。通过深入分…

使用Arthas查看方法的参数信息情况

使用Arthas查看方法的参数信息情况 前言 最近在排查一个bug&#xff0c;需要看看一个接口方法的传参&#xff0c;但是方法里并没有打印传参&#xff0c;而且还是生产环境&#xff0c;更新包也麻烦&#xff0c;所以&#xff0c;准备安装一下Arthas&#xff0c;通过Arthas可以做…

windows 11中如何设置默认为英文输入法

由于工作需要&#xff0c;我一直在windows7下使用VB6&#xff0c;以前尝试着使用新的系统&#xff0c;但都无法正常安装vb&#xff0c;最近几天由于系统一次作死操作&#xff0c;逼着我安装了win11&#xff0c;并且在其上正常安装了vb6&#xff0c;本想着十分高兴&#xff0c;终…

Ascend C 2.0新特性详解,支撑大模型融合算子高效开发

近日&#xff0c;昇腾算子编程语言Ascend C发布2.0版本&#xff0c;新增支持通算融合MC特性&#xff0c;使能大模型场景下通信和计算并行&#xff0c;提高整网运行性能&#xff1b;提供更丰富的API覆盖当前主流的融合算子开发场景&#xff0c;提升开发效率&#xff1b;同时通过…

大语言模型 (LLM) 红队测试:提前解决模型漏洞

大型语言模型 (LLM) 的兴起具有变革性&#xff0c;以其在自然语言处理和生成方面具有与人类相似的卓越能力&#xff0c;展现出巨大的潜力。然而&#xff0c;LLM 也被发现存在偏见、提供错误信息或幻觉、生成有害内容&#xff0c;甚至进行欺骗行为的情况。一些备受关注的事件包括…

clipboard.js(web页面实现点击复制)

文章目录 codeshow 一个很简单的需求&#xff0c;一个单页面需要一个点击复制的功能 后来在线上找到一个clipboard.js可以实现&#xff0c;这里只用到了最基础的用法&#xff0c;页面样式布局基于bootstrap5.2.3 code <div class"d-flex align-items-center justify-co…

字符设备驱动

目录 demo.c test.c led.h makefile 实验效果 demo.c #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/io.h> #include "myled.h" //内核buf char kbuf[…

UITableView之cell复用

关于cell复用的必要性 cellForRowAtIndexPath会随着屏幕滚动而调用&#xff0c;每次出现新行时因为行号变化&#xff0c;就会被调用。 底层原理&#xff1a;当前单元格滚出屏幕时cell销毁&#xff0c;当前单元格又滚回来时cell创建。短时间内频繁创建和销毁cell会影响系统性能…

MySQL 触发器(实验报告)

一、实验名称&#xff1a; 触发器 二、实验日期&#xff1a; 2024 年 6月 8日 三、实验目的&#xff1a; 掌握MySQL触发器的创建及调用&#xff1b; 四、实验用的仪器和材料&#xff1a; 硬件&#xff1a;PC电脑一台&#xff1b; 配置&#xff1a;内存&#xff0c;…

React Native将 ipad 端软件设置为横屏显示后关闭 Modal 弹窗报错

问题&#xff1a; 将 ipad 端软件设置为横屏显示后&#xff0c;关闭 Modal 弹窗报错。 Modal was presented with 0x2 orientations mask but the application only supports 0x18.Add more interface orientations to your apps Info.plist to fix this.NOTE: This will cras…

幸狐RV1106开发板烧录Ubuntu系统与配置SDK,RV1106 LuckFox Pico Max——最新的操作

资料&#xff1a;上手教程 | LUCKFOX WIKI 以及SDK内的文档资料 开发板型号&#xff1a;RV1106 LuckFox Pico Max 烧录系统&#xff1a; Ubuntu 虚拟机系统&#xff1a;Ubuntu 20.04&&Ubuntu22.04 PC系统&#xff1a;win11 占用空间&#xff1a;大概15G 本文主要记…

基于jeecgboot-vue3的Flowable流程-流程处理(一)

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 这部分修正一些流程处理中VForm3线上的一些bug问题 1、初始化流程提交与现实的前端页面代码 <!--初始化流程加载默认VForm3表单信息--><el-col :span"16" :offset&qu…

在 Selenium 中更改 User-Agent | 步骤与最佳实践

在 Selenium 中更改 User Agent 是许多网页抓取任务中的关键步骤。它有助于将自动化脚本伪装成常规浏览器&#xff0c;从而避免被网站检测到。本指南将带您了解如何在 Selenium 中更改 Google Chrome 的 User Agent&#xff0c;并提供最佳实践以确保您的网页抓取任务顺利进行。…