【堆】Top-K问题

标题C语言库函数scanf()解读

水墨不写bug 


 (图片来源于网络)

正文开始:

        Top-K问题是一类问题的统称:

        即根据对象的某一属性,找出这个属性最突出的K个对象,并且通常对象的数量极大。

(一)构造对象

        为演示方便,通过代码生成一个数据量极大的数据对象:生成1w个数据;如果我们将问题抽象化为找出1w个数据中的最大5个数,那么Top-K问题就切实可见了。

        在你的编译器上输入如下代码并运行,你会发现在同目录下多了一个文件:"data.txt"

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


void CreateNDate()
{
	// 造数据
	int n = 10000;
	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() % 1000000;
		fprintf(fin, "%d\n", x);
	}

	fclose(fin);
}

再打开文件,人为的将5个数改写为最大并做好记录。

(二)建堆并解决过程

        打开文件并读入数据,读入并压入k个数据,这时堆内已有k个数据;

        接下来每读入一个数据,就需要堆顶部的数据进行比较;

(找出最大K个数:这种情况需要建立小堆,因为小堆的堆顶数据是这个堆里最小的:最小的数与读入的数进行比较。如果读入的数比较大,就将较小的堆顶交换出去;如果读入的数比较小,就保留堆顶的数据)

(找出最小K个数:这种情况需要建立大堆,因为大堆的堆顶数据是这个堆里最大的:最大的数与读入的数进行比较。如果读入的数比较小,就将较大的堆顶交换出去;如果读入的数比较大,就保留堆顶的数据)

         这样既能利用堆的高效插入和高效删除又避免了开辟大量的空间来存储变量

 


#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

typedef int HPDatatype;

typedef struct Heap
{
	HPDatatype* a;
	int size;
	int capacity;
}HP;
void AdgustDown(HPDatatype* a, int n, int parent);

//初始化
void HPinit(HP* php);

//销毁
void HPDestroy(HP* php);

//插入数据
void HPpush(HP* php, HPDatatype x);

//删除数据,规定删除堆顶的数据
void HPpop(HP* php);

//得到堆顶数据
HPDatatype HPtop(HP* php);

//判空
bool HPempty(HP* php);

//数据个数
int HPsize(HP* php);

//初始化
void HPinit(HP* php)
{
	assert(php);
	php->capacity = php->size = 0;//size指向最后一个元素的下一个
	php->a = NULL;
}

//销毁
void HPDestroy(HP* php)
{
	assert(php);
	php->capacity = php->size = 0;
	free(php->a);
}

//交换函数
void Swap(HPDatatype* a,HPDatatype* b)
{
	HPDatatype tem = *a;
	*a = *b;
	*b = tem;
}

//此处模拟实现小堆,小数上移
//参数:
//要插入的数据和孩子节点的数组下标
void AdgustUp(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 HPpush(HP* php, HPDatatype x)
{
	assert(php);
	//如果size == capacity 说明顺序表满了,或者没有一个数据
	if (php->size == php->capacity)
	{
		int Newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDatatype* tem = (HPDatatype*)realloc(php->a, sizeof(HPDatatype)*Newcapacity);
		{
			if (tem == NULL)
			{
				perror("realloc fail!");
				return;
			}
		}
		php->a = tem;
		php->capacity = Newcapacity;
	}

	//开辟好空间后,插入
	php->a[php->size] = x;
	php->size++;
	//向上调整
	AdgustUp(php->a,php->size-1);
}

//向下调整
//参数;
//顺序表,数据总个数,向下调整时目前双亲节点的数组下标
void AdgustDown(HPDatatype* a,int n,int parent)
{
	int child = parent * 2 + 1;
	//目前假设左孩子小
	while (child < n)
	{	//如果右孩子存在,并比左孩子小,选择右孩子
		if (child + 1 < n && 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 HPpop(HP* php)
{
	assert(php);
	Swap(&php->a[0], &php->a[php->size - 1]);
	--php->size;
	AdgustDown(php->a,php->size,0);
}

//得到堆顶数据
HPDatatype HPtop(HP* php)
{
	assert(php);
	return php->a[0];
}

//判空
//若为空,返回真,否则返回假
bool HPempty(HP* php)
{
	assert(php);
	return php->size == 0;
}

//数据个数
int HPsize(HP* php)
{
	assert(php);
	return php->size;
}

int main()
{
	
	//CreateNDate();
	FILE* fin = fopen("data.txt", "r");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}
	int comp = 0;
	HP ph;
	HPinit(&ph);
	int k = 0;
	scanf("%d", &k);
	//刚开始push k次,建堆
	for (int i = 0; i < k; i++)
	{
		fscanf(fin, "%d", &comp);
		HPpush(&ph, comp);
	}
	//开始比较,comp与堆顶数据的大小
	while (fscanf(fin,"%d",&comp) != EOF)
	{
		if (ph.a[0] < comp)
		{
			Swap(&ph.a[0], &comp);
			AdgustDown(ph.a,ph.size,0);
		}
	}
	for (int i = 0; i < k; i++)
	{
		printf("%d ", ph.a[i]);
	}

	return 0;
}

        (注意:        先调用CreateNDate()函数来创建一个数据文件,标记好最大K个数后再运行上述代码来验证。)


完~

未经作者同意禁止转载

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

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

相关文章

简单了解多线程

并发和并行 并发&#xff1a; 在同一时刻&#xff0c;多个指令在单一CPU上交替指向 并行&#xff1a;在同一时刻&#xff0c;多个指令在多个CPU上同时执行 2核4线程&#xff0c;4核8线程&#xff0c;8核16线程&#xff0c;16核32线程 基础实现线程的方式 Thread :继承类 &…

OpenJDK11的安装及配置

OpenJDK11的安装及配置: 下载链接&#xff1a;http://jdk.java.net/archive/ 1. 下载 点击链接下载OpenJDK11的zip压缩文件** 选择Windows版本 解压 解压成功。 2. 环境配置 打开设置----选择相关设置中的高级系统设置 选择高级—环境变量 系统变量 下添加JAVA_HOME …

开发技术-FeignClient 对单个接口设置超时时间

1. 背景 FeignClient 调用某个接口&#xff0c;3s 没有结果就需要停止&#xff0c;处理后续业务。 2. 方法 FeignClient 自定义 name 属性 FeignClient(name "aaa" , url "xxx") public interface TestApi {ResponseBodyPOSTMapping(value "xx…

STM32编程控制电机实现PID速度闭环中的堵转检测

实现PID速度闭环控制是编码器电机驱动中的重要任务&#xff0c;而堵转检测和控制则是保证电机正常运行的关键环节。在本文中&#xff0c;我们将详细探讨STM32编程驱动编码器电机实现PID速度闭环控制中堵转检测和控制的方法。 一、堵转检测方法 编码器反馈&#xff1a; 编码器…

软考 系统架构设计师系列知识点之系统性能(1)

所属章节&#xff1a; 第2章. 计算机系统基础知识 第9节. 系统性能 系统性能是一个系统提供给用户的所有性能指标的集合。它既包括硬件性能&#xff08;如处理器主频、存储器容量、通信带宽等&#xff09;和软件性能&#xff08;如上下文切换、延迟、执行时间等&#xff09;&a…

【零基础C语言】内存中的存储

一. 整数在内存中的存储 1.原码反码补码 在计算机中整数在内存中存储的是二进制数 二进制的存储有三种表示的方式: 原码反码补码 这三种表示方式又分为符号位和数值位&#xff1a; 符号位中0表示正数&#xff0c;1表示负数&#xff0c;最高位被当作符号位&#xff0c;其他为…

DML - 增删改(insert into,delete,update)

引言&#xff1a;对比DB / 表结构 : create , drop , alter 本次记录 数据操作 语言&#xff1a; 1.进入 hive 数据库&#xff0c;再打开 ryx1 表 2. insert select 3. update select 4. delete select

JVM学习-类加载

目录 1.类文件结构 2.类加载器 3.类加载的三个阶段 3.1加载 3.2链接 3.2.1验证 3.2.2准备阶段 3.2.3解析阶段 3.3初始化 4.拓展&#xff1a;反射 4.1获取类对象 4.2创建实例 4.3获取方法 4.4方法调用 1.类文件结构 2.类加载器 类加载器用来将类文件的二进制字节码加载到JV…

3 CUDA硬件概述

3.1 PC 架构 首先&#xff0c;我们看看当下许多PC中都使用的酷睿2(Core2)处理器的典型架构&#xff0c;然后分析一下它是如何影响我们使用GPU 加速器的(如图 3-1所示)。 图3-1典型的酷睿2(Core2)系列处理器的结构图 由于所有的 GPU 设备都是通过 PCI-E(Peripheral Communicat…

修改约束

目录 修改约束 创建数据库 添加约束 删除约束 Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 修改约束 如果说表结构的修改还在可以容忍的范畴之内&#xff0c;那么约束的修改是绝对 100% 禁止的 所有的约束一定要在…

王者荣耀使用的UDP通信,十几年编程没用过的协议

缘起 最近在查阅moba相关的资料时&#xff0c;看到了一篇王者荣耀的研发同学的技术分享&#xff0c;从文章中了解到王者荣耀的通信方式是UDP通信&#xff0c;回想到整个职业生涯&#xff0c;貌似并没有用过&#xff0c;今天特地整理下。 udp技术细节 udp协议 UDP协议叫做用…

代码随想录算法训练营三刷day29 | 回溯算法之 491.递增子序列 46.全排列 47.全排列 II

三刷day29 491.递增子序列回溯三部曲 46.全排列回溯三部曲 47.全排列 II 491.递增子序列 题目链接 解题思路&#xff1a; 回溯三部曲 递归函数参数 本题求子序列&#xff0c;很明显一个元素不能重复使用&#xff0c;所以需要startIndex&#xff0c;调整下一层递归的起始位…

c语言指针(二)

文章目录 c语言指针&#xff08;二&#xff09;1.数组名的理解2.使用指针访问数组3.一维数组的传参本质 c语言指针&#xff08;二&#xff09; 1.数组名的理解 int arr[10] { 1,2,3,4,5,6,7,8,9,10 }; int* p &arr[0]这⾥我们使⽤ &arr[0] 的⽅式拿到了数组第⼀个元…

系统设计实例(一)百万级别用户系统

二、百万级别用户系统 原则&#xff1a; 尽可能地缓存数据采用无状态Web层支持多个数据中心在 CDN 中托管静态资源通过分片扩展数据层将层级拆分为独立的服务 负载均衡器 负载均衡器会将传入的流量均匀分配给在负载均衡集合中定义的Web服务器&#xff0c;用户直接连接负载均…

【C++ leetcode】双指针问题(续)

3. 202 .快乐数 题目 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到 1。如果这个过程 结…

文件IO (File对象, 文件读写)

文件 狭义的文件: 硬盘上的文件和目录(文件夹) 广义的文件: 泛指计算机中的很多 软硬件资源 OS 中, 把很多硬件设备和软件资源抽象成了文件, 按照文件的方式来统一管理网络编程中, OS 把网卡当成了一个文件来操作 路径 绝对路径: 以盘符**(c: d: e:)**开头的路径 相对路径: 以 …

HarmonyOS ArkTS 通用事件(二十三)

通用事件目录 点击事件事件ClickEvent对象说明EventTarget8对象说明示例 触摸事件事件TouchEvent对象说明TouchObject对象说明示例 挂载卸载事件事件示例 点击事件 组件被点击时触发的事件。 事件 ClickEvent对象说明 从API version 9开始&#xff0c;该接口支持在ArkTS卡片中…

C++:继承:面向对象编程的重要特性

(❁◡❁)(●◡●)╰(*▽*)╯(*/ω&#xff3c;*)(^///^)(❁◡❁)(❁◡❁)(●◡●)╰(*▽*)╯(*/ω&#xff3c;*)(❁◡❁)(●’◡’●)╰(▽)╯(/ω&#xff3c;)(///) C&#xff1a;继承&#xff1a;面向对象编程的重要特性 前言**继承**1.继承的概念及定义1.1继承的概念1.2继…

学完Python的7大就业方向,哪个赚钱最多?

“ 我想学Python&#xff0c;但是学完Python后都能干啥 &#xff1f;” “ 现在学Python&#xff0c;哪个方向最简单&#xff1f;哪个方向最吃香 &#xff1f;” “ …… ” 相信不少Python的初学者&#xff0c;都会遇到上面的这些问题。大家都知道Python很吃香&#xff0c;薪资…

英伟达 V100、A100/800、H100/800 GPU 对比

近期&#xff0c;不论是国外的 ChatGPT&#xff0c;还是国内诸多的大模型&#xff0c;让 AIGC 的市场一片爆火。而在 AIGC 的种种智能表现背后&#xff0c;均来自于堪称天文数字的算力支持。以 ChatGPT 为例&#xff0c;据微软高管透露&#xff0c;为 ChatGPT 提供算力支持的 A…