数据结构:堆的三部曲(二)top K问题

news2024/9/24 23:31:11

堆的应用(一)top K问题

  • 一.top k问题的应用本质解析
  • 二.top K问题使用案例——从100亿整型的文件中找出前5个最大值
    • 1.建堆
      • 1.1过程分析
      • 1.2过程图模拟
      • 1.3向上调整算法代码
      • 1.4建堆代码
    • 2.处理文件中剩余剩余元素
      • 2.1过程分析
      • 2.2过程图示例
      • 2.3向下调整算法代码
      • 2.4处理后续元素代码
  • 三.附录源码

一.top k问题的应用本质解析

top k问题解决的是获取前几个最值的问题。
我们知道堆的功能主要是选数,选出最大值或者最小值。那么我们每次获取堆顶元素后,再将剩余元素调整成堆,就可以选出次大的数,如果我们只想要前k个最大值或者最小值,就只需要获取堆顶元素k次,调整k次。比如王者荣耀中的国服榜单,需要的是这个英雄的前十名,我们只需要获取前10名即可。

学习了堆的实现,我们对于比较少的数据可以采用建堆一直push的方式,再取堆顶元素再pop从而找到次大值的方法,循环k次上述操作即可实现。但是如果数据非常多,那么malloc申请空间会失败,此时堆无法建立开辟,此时我们应该如何解决呢?

以找前k个最大值为例:
上面的问题是因为内存不足,为了解决这个问题,这里我们可以从文件中读取k个数据并且建一个小堆,然后再依次处理剩余的元素。

为什么要建小堆?
这里很多人可能第一印象就是建立大堆,从而选出前k个最大值,但是我们想一个问题,如果最大的值在前k个数中,那么最大值就会在堆顶,那么其他k-1个需要找的值就都会比堆顶小,导致无法入堆。
因此我们需要建小堆,如果比堆顶的元素大,那么就和堆顶的元素交换,然后再调整,这样的话最大值就会沉入堆底,直到前k个最大的数都进入堆中。

总结:前k个最小值建大堆,前k个最大值建小堆

我们通过以下的案例来学习解决这一问题。

二.top K问题使用案例——从100亿整型的文件中找出前5个最大值

由一的分析我们可以得知需要建小堆。

我们首先先创建足够多的整形随机数,并保存在文件中。为了验证我们的top k是否正确,我们将随机值取模于一个10000000,保证数据都在这个范围内,然后我们自己手动插入几个“探子”,我们在该txt文件中改变几个值的大小为超过10000000,这样我们就可以通过这几个“探子”是否被找到从而验证是否正确。

在读取前k个元素时顺便建堆,我们采取向上调整建堆的方法。
向上调整建堆:模拟堆的插入的思想,我们使用向上调整算法来建堆(详情请看上篇:堆的实现)

1.建堆

1.1过程分析

由于向上调整算法和向下调整算法的前提是插入前仍然是堆,插入后改变了堆的性质。因此首先将第一个元素看作堆,然后后面的元素模拟依次入堆的操作,也就是将后面元素的下标依次作为最后一个节点的坐标传入向上调整算法函数之中,这样的话每次向上调整传入下标前面的值为堆,从而满足前提条件。

1.2过程图模拟

在这里插入图片描述
在这里插入图片描述

1.3向上调整算法代码

void AdjustUp(Hpdatatype* a, int child)
{
	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;
		}
	}
}

1.4建堆代码

	int* heap = (int*)malloc(sizeof(int) * k);
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &heap[i]);
		//从文件中读取数据存储到heap中去
		AdjustUp(heap, i);
		//开始调整
	}

2.处理文件中剩余剩余元素

2.1过程分析

依次读取文件中的数据,并存储在变量tmp之中,再将其和和堆顶元素比较,比堆顶大,就和堆顶交换(堆顶是最小值,如果它比tmp小,就证明它不是我们需要找的元素),然后再向下调整,如果比堆顶元素小,则不需要入堆,。直到文件读取结束。

2.2过程图示例

和4比较,不需要入堆,如图:
在这里插入图片描述
和20比较,需要入堆,如图:

在这里插入图片描述

2.3向下调整算法代码

void AdjustDown(Hpdatatype* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && a[child] > a[child + 1])
		{
			child++;
		}
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

2.4处理后续元素代码

	int tmp = 0;//存储读取的值
	while (fscanf(fout, "%d", &tmp) != EOF)
	{
		if (tmp > heap[0])
		{
			Swap(&tmp, &heap[0]);
			AdjustDown(heap, k, 0);
		}
	}

三.附录源码

向上调整算法代码:

void AdjustUp(Hpdatatype* a, int child)
{
	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(Hpdatatype* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && a[child] > a[child + 1])
		{
			child++;
		}
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

创建数据代码如下:

void creatInform()
{
	int n = 10000000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}

	for (int i = 0; i < n; ++i)
	{
		int x = (rand() + i) % 10000000;
		fprintf(fin, "%d\n", x);
	}

	fclose(fin);
}

测试代码如下:

void testTopK(const char* file, int k)
{
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error");
		return;
	}

	int* heap = (int*)malloc(sizeof(int) * k);
	//建堆
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &heap[i]);
		AdjustUp(heap, i);
	}

	int tmp = 0;
	while (fscanf(fout, "%d", &tmp) != EOF)
	{
		if (tmp > heap[0])
		{
			Swap(&tmp, &heap[0]);
			AdjustDown(heap, k, 0);
		}
	}

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

以上是本次所有内容,谢谢观看。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

百度百科词条创建多久可以通过?

一个优质的百度百科词条&#xff0c;能提升个人或企业的品牌形象。因此&#xff0c;越来越多的人希望创建自己的百度百科词条&#xff0c;那么&#xff0c;创建一个百度百科词条到底需要多久才能通过审核呢&#xff1f;接下来伯乐网络传媒就来给大家分享一下。 一、百度百科词条…

AI计算,为什么要用GPU?

今天这篇文章&#xff0c;我们继续来聊聊芯片。 在之前的文章里&#xff0c;小枣君说过&#xff0c;行业里通常会把半导体芯片分为数字芯片和模拟芯片。其中&#xff0c;数字芯片的市场规模占比较大&#xff0c;达到70%左右。 数字芯片&#xff0c;还可以进一步细分&#xff0…

灰度发布及声明式资源管理(yaml文件)

一、三种常见的项目发布方式 1&#xff09;蓝绿发布 2&#xff09;灰度发布【常用】 3&#xff09;滚动发布 应用程序升级&#xff0c;面临最大的问题是新旧业务之间的切换 立项-定稿-需求发布-开发-测试-发布&#xff0c;测试上线后&#xff0c;再完美也会有问题&#xff0c;为…

【Mybatis】深入学习MyBatis:高级特性与Spring整合

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; Mybatis ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 高级特性 1 一级缓存和二级缓存 一级缓存 二级缓存 2 延迟加载 5 整合Spring 1 MyBatis-Spring模块 2 事务管理 结…

C#基础:字段的初始化、继承

一、字段的初始化 class Test {static int Main(string[] args){var list new List<Calculate>();//1.Calculate中加入abvar calculate new Calculate { a 2, b 3 };//2.Calculate中加入Value列表calculate.Values.Add(new Value { id 1, value 6 });calculate.Va…

计算机组成原理-期末复习

目录 第一章——计算机系统概述 一、数字计算机的主要组成结构 二、指令的形式 三、控制器的基本任务 四、指令流和数据流 五、适配器与输入/输出设备 七、计算机的系统软件 八、C 语言的转换层次图 九、计算机系统的层次结构图 第二章——运算方法和运算器 一、 数据格式…

MySQL之表的记录操作

前言 存数据不是目的&#xff0c;目的是能够将存起来的数据取出来或者查出来&#xff0c;并且能够对数据进行增删改查操作&#xff0c;本文将详细介绍表中记录的增删改查操作。对记录的操作属于DML数据库操作语言&#xff0c;可以通过SQL实现对数据的操作&#xff0c;包括实现向…

爬虫入门与urllibrequests

前情摘要 一、web请求全过程剖析 我们浏览器在输入完网址到我们看到网页的整体内容, 这个过程中究竟发生了些什么? 我们看一下一个浏览器请求的全过程 接下来就是一个比较重要的事情了. 所有的数据都在页面源代码里么? 非也~ 这里要介绍一个新的概念 那就是页面渲染数据的…

爬取涛声网音频

代码展现&#xff1a; 代码详情&#xff1a; import requests import re import os filename 声音// if not os.path.exists(filename): os.mkdir(filename) def down_load(page): for page in range(page): page page1 url https://www.tosound.…

如何将图像数据转换为.mat文件,mat文件内是cell封装的struct格式的数据

在我看论文&#xff1a;《 Holistically-nested Edge Detection (HED) 》的时候&#xff0c;对论文中有关边缘结果的评价指标很感兴趣&#xff0c;于是我就研究了如何计算这些指标 如果有同样感兴趣或者有需要的小伙伴可以下载这里的代码&#xff1a;GitHub - xwjabc/hed: A P…

IO进程线程 day4 文件IO与目录操作

1.使用标准IO完成两个文件拷贝 #include <head.h> int main(int argc, const char *argv[]) {//判断输入是否合法if(argc>3){printf("输入不合法\n");return -1;}//定义两个文件指针&#xff0c;用于读写FILE *fp1NULL;FILE *fp2NULL;if((fp1fopen(argv[1]…

扩展:键盘录入笔记(next()、nextLine()、nextInt()、nextDouble())

文章目录 一&#xff0c;键盘录入涉及到的方法如下&#xff1a;1&#xff09;next&#xff08;&#xff09;、nextLine&#xff08;&#xff09;&#xff1a;代码示例&#xff1a;代码示例&#xff1a; 2&#xff09;nextInt&#xff08;&#xff09;&#xff1a;代码示例&…

面试题:Linux命令大全

序号命令对应英文作用1ls [目录名]list查看当前目录下的内容2pwdprint work directory查看当前所在目录3cd [目录名]change directory切换目录4touch [文件名]touch如果文件不存在,创建文件5mkdir [目录名]make directory创建目录6rm [文件名]remove删除指定文件 ls 查看当前目…

hAdmin漂亮的后台html模板免费下载

hAdmin漂亮的后台html模板免费下载-遇见你与你分享

Qt6.5类库详解:QFontComboBox

哈喽大家好&#xff0c;欢迎关注公众号(20YC编程)&#xff0c;有免费视频课程哦&#xff01; -今日内容- 1 QFontComboBox介绍 QFontComboBox是Qt框架中的一个字体选择类&#xff0c;它提供了一个下拉列表框&#xff0c;用于选择字体。 QFontComboBox的主要功能和特点&#x…

Java基础-----集合类(四)

文章目录 1. Iterator和ListIterator1.1 简介1.2 常用方法 2. remove方法2.1 比较foreach方式和迭代器方式删除元素2.2 找原因 -- 迭代器删除操作源码 1. Iterator和ListIterator 1.1 简介 1.Iterator 可以遍历List集合&#xff0c;也可以遍历Set集合&#xff1b; ListIterat…

Unity SVN更新提交小工具

Unity SVN更新提交小工具 前言使用说明必要前提源码参数说明 感谢 前言 Unity开发时每次都要到文件夹中操作SVN&#xff0c;做了一个小工具能够在Editor中直接操作。 使用说明 必要前提 前提是要安装好SVN&#xff0c;在文件夹右键能够看到安装的SVN 源码 using System…

音效出众设计时尚,内置AI功能,sanag塞那Z50上手

现在蓝牙耳机已经成为人们生活中不可或缺的一部分了&#xff0c;像是在上班、坐车的时候&#xff0c;既可以享受自己的音乐空间&#xff0c;又不会吵到别人&#xff0c;看书、做题还是运动的时候&#xff0c;也可以保证长时间使用耳朵卫生、舒适度。正因为庞大的市场需求&#…

软件测试作业‖pytest+po+csv+html报告+cookie+selenium

软件测试作业‖pytestpocsvhtml报告cookieselenium 先用本地部署的系统试了下 或者UFT自动化测试里诺图书管理系统软件测试 # &#xff0c;#测试报告# #性能测试#&#xff0c;#测试用例#&#xff0c; #自动化测试# Selenium 的 Web自动化测试基本要求和注意事项 1.请使用ch…

Mysql的四大引擎,账号管理,数据库的建立

数据库存储引擎查看 Support字段说明 default的为默认引擎 YES表示可以使用 NO表示不能使用 命令 SHOW ENGINES 四大引擎 MEMORY 使用场景&#xff1a;由于易失性&#xff0c;可以用于存储在分析中产生的中间表 特点 所有的数据都保存在内存中&#xff0c;一旦服务器重启&…