堆的应用2——TOPK问题

news2024/11/17 13:36:14

TOPK问题

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

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

情况1——数据量小

对于Top-K问题,能想到的最简单直接的方式就是排序,

就是我们建N个数的大堆,再Pop K次就行了

 代码展示(最大的K个)

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>
typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;
void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType x = *p1;
	*p1 = *p2;
	*p2 = x;
}

//堆的向下调整(大堆)  
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1; // 计算左子节点的索引  

	// 当 child 索引在数组范围内时执行循环  
	while (child < n)
	{
		// 如果右子节点存在且大于左子节点  
		if (child + 1 < n && a[child + 1] > a[child])
		{
			++child; // 更新 child 为右子节点的索引  
		}

		// 如果 child 节点(现在是左右子节点中较大的一个)大于 parent 节点  
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]); // 交换 parent 和 child 的值  
			parent = child; // 更新 parent 为刚刚交换过的 child 的索引  
			child = parent * 2 + 1; // 重新计算左子节点的索引  
		}
		else
		{
			break; // child 节点不大于 parent 节点,无需继续调整,退出循环  
		}
	}
}

void HeapInit(HP* php)
{
	assert(php);

	php->a = (HPDataType*)malloc(sizeof(HPDataType) * 4);
	if (php->a == NULL)
	{
		perror("malloc fail");
		return;
	}

	php->size = 0;
	php->capacity = 4;
}

bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}
void HeapDestroy(HP* php)
{
	assert(php);

	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}




// 除了child这个位置,前面数据构成堆
void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	//while (parent >= 0)
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}



void HeapPush(HP* php, HPDataType x)
{
	assert(php);

	if (php->size == php->capacity)
	{
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * php->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		php->a = tmp;
		php->capacity *= 2;
	}

	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}

//打印堆
void HeapPrint(HP* php)
{
	assert(php);
	//按照物理结构进行打印
	int i = 0;
	for (i = 0; i < php->size; i++)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}

void HeapPop(HP* php)
{
	assert(php);
	assert(!HeapEmpty(php));

	// 删除数据
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;

	AdjustDown(php->a, php->size, 0);
}

HPDataType HeapTop(HP* php)
{
	assert(php);
	return php->a[0];
}



int HeapSize(HP* php)
{
	assert(php);
	return php->size;
}


int main()
{
	HP hp;
	HeapInit(&hp);
	HeapPush(&hp, 4);
	HeapPush(&hp, 180);
	HeapPush(&hp, 42);
	HeapPush(&hp, 12);
	HeapPush(&hp, 21);
	HeapPush(&hp, 3);
	HeapPush(&hp, 1);
	HeapPush(&hp, 2);
	HeapPush(&hp, 50);
	HeapPush(&hp, 5);
	HeapPush(&hp, 5);
	HeapPush(&hp, 150);
	HeapPush(&hp, 5);
	HeapPush(&hp, 45);
	HeapPush(&hp, 5);

	int k = 0;
	scanf("%d", &k);
	while (!HeapEmpty(&hp) && k--)
	{
		printf("%d ", HeapTop(&hp));
		HeapPop(&hp);
	}
	printf("\n");

	return 0;
}

情况2——数据量大 

但是:如果数据量非常大,排序就不太可取了(可能 数据都不能一下子全部加载到内存中)。

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

1. 用数据集合中前K个元素来建堆

  • 前k个最大的元素,则建小堆
  • 前k个最小的元素,则建大堆

有人就好奇了,为什么找最大的要建小堆而不是大堆,原因很简单,

建小堆,最大的K个元素肯定可以进去,但是如果建的是大堆的话,假设前K个里就遇到了最大的,它就会阻止后面其他次大的元素进去堆

2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

将剩余N-K个元素依次与堆顶元素比,如果这个数据比堆顶的数据大,就替代它进堆,遍历完后堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

代码展示

代码分两步执行

我们先来看看源码

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

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;
void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType x = *p1;
	*p1 = *p2;
	*p2 = x;
}

//堆的向下调整(小堆)—— 左右子树都是小堆
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 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 = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void PrintTopK(const char* file, int k)
{
	// 1. 建堆--用a中前k个元素建小堆
	int* topk = (int*)malloc(sizeof(int) * k);
	assert(topk);

	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error");
		return;
	}

	// 读出前k个数据建小堆
	for (int i = 0; i < k; ++i)
	{
		fscanf(fout, "%d", &topk[i]);
	}

	for (int i = (k - 2) / 2; i >= 0; --i)
	{
		AdjustDown(topk, k, i);
	}

	// 2. 将剩余n-k个元素依次与堆顶元素交换,不满则则替换
	int val = 0;
	int ret = fscanf(fout, "%d", &val);
	while (ret != EOF)
	{
		if (val > topk[0])
		{
			topk[0] = val;
			AdjustDown(topk, k, 0);
		}

		ret = fscanf(fout, "%d", &val);
	}

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

	free(topk);
	fclose(fout);
}

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

	fclose(fin);
}

第一步创建数据 

int main()
{
	CreateNDate();
	return 0;
}

我们先运行一下上面的代码,然后就能在目录下看到我们创建了一个txt文件 

这里就有我们要的超大量数据,我们可以把它添到目录下来,方便修改

我们创建的都是10000以内的数据,

第二步——修改数据 

int main()
{
	PrintTopK("data.txt",10);
	return 0;
}

我们先运行一下

确认都在10000内之后,就去修改数据

再运行上面的代码,得到下面结果 

 我们发现,修改后的7个数据全在里面,也就说明我们的代码没有问题

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

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

相关文章

品鉴中的文化碰撞:如何理解和欣赏不同文化背景下的红酒

红酒作为世界各地广泛生产的产品&#xff0c;具有丰富的文化内涵。不同国家、地区和民族的红酒文化各具特色&#xff0c;反映了当地的历史、传统、习俗和生活方式。在品鉴云仓酒庄雷盛红酒时&#xff0c;理解和欣赏不同文化背景下的红酒是提升品鉴体验的重要一环。 首先&#x…

【Java orm 框架比较】九 新增wood框架对比

【Java orm 框架比较】九 新增wood框架对比 本次新增wood 框架测试 测试数据存储、分页查询&#xff0c;文档及框架比较稳定半天时间加入测试使用 迁移到&#xff08;https://gitee.com/wujiawei1207537021/spring-orm-integration-compare&#xff09; orm框架使用性能比较…

【STM32 |GPIO】GPIO结构、GPIO输出

目录 GPIO简介 GPIO的基本结构 GPIO位结构&#xff08;每一位的具体电路结构&#xff09; 输入 上拉和下拉电阻 斯密特触发器 ​编辑 输出 GPIO模式 ​编辑 浮空输入、上拉输入、下拉输入 模拟输入 开漏输出和推挽输出 复用开漏输出和复用推挽输出 LED和蜂鸣器…

代码随想录算法训练营第36期DAY22

DAY22 654最大二叉树 自己做的时候忽略了&#xff1a;nums.length>1的题给条件。所以每次递归都要判断是否size()>1&#xff0c;不要空的。 /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *rig…

JSpdf,前端下载大量表格数据pdf文件,不创建dom

数据量太大使用dom》canvas》image》pdf.addimage方法弊端是canvas超出 浏览器承受像素会图片损害&#xff0c;只能将其切割转成小块的canvas,每一次调用html2canvas等待时间都很长累积时间更长&#xff0c;虽然最终可以做到抽取最小dom节点转canvas拼接数据&#xff0c;但是死…

[附源码+视频教程]暗黑纪元H5手游_架设搭建_畅玩三网全通西方3D世界_带GM

本教程仅限学习使用&#xff0c;禁止商用&#xff0c;一切后果与本人无关&#xff0c;此声明具有法律效应&#xff01;&#xff01;&#xff01;&#xff01; 教程是本人亲自搭建成功的&#xff0c;绝对是完整可运行的&#xff0c;踩过的坑都给你们填上了 一. 演示视频 暗黑纪…

Android 查看CUP占用率

查看每个进程CUP占用率的几种方式,由于自己充电界面老是导致整机温度过高&#xff0c;后面发现自己的线程一直在跑&#xff0c;相当于死循环&#xff0c;后面加上sleep才得以改善&#xff1b;先看看几种查询方式吧。 1、adb shell top 2、adb shell busybox top 3、adb shell …

C++类和对象中篇

&#x1f407; &#x1f525;博客主页&#xff1a; 云曦 &#x1f4cb;系列专栏&#xff1a;[C] &#x1f4a8;路漫漫其修远兮 吾将而求索 &#x1f49b; 感谢大家&#x1f44d;点赞 &#x1f60b;关注&#x1f4dd;评论 文章目录 &#x1f4d4;前言&#x1f4d4;1、类的六个…

微服务学习笔记

微服务学习笔记 文章目录 微服务学习笔记认识微服务微服务技术栈微服务学习要点微服务远程调用1)注册RestTemplate2) 服务远程调用RestTemplate Eureka注册中心简介操作过程搭建EurekaServer注册user-service在order-service完成服务拉取 Ribbon负载均衡IRule负载均衡策略饥饿加…

在线扭蛋机小程序:商家稳占市场的新突破口

近几年&#xff0c;扭蛋机进入了爆发期&#xff0c;动漫、游戏的发展更是推动了市场的发展&#xff0c;我国扭蛋机正在蓬勃发展中。 不过&#xff0c;在市场规模扩大下&#xff0c;扭蛋机行业的竞争力也在同时加大&#xff0c;企业商家需要在市场竞争中寻求发展新思路&#xf…

SEQUENTIAL CONSISTENCY----SC

SC模型是最直观的memory consistency model; 什么是single core sequential? 真正的执行顺序&#xff0c;和PO的顺序&#xff0c;是相同的&#xff1b; 什么是multi core sequential consistent? the operations of each individual processor (core) appear in this seq…

【知识碎片】2024_05_08

记录了两个代码和一个C语言switch的题。代码【错误的集合】使用到了hash&#xff0c;【密码检查】是对多个字符串输入的检查&#xff0c;有一些条件细节。两个代码都是关于数组内容操作的题目。 每日代码 1.错误的集合 错误的集合-力扣&#xff08;LeetCode 也是数组的操作&…

EOS智慧营销系统:突破传统,引领智慧营销新潮流

在数字化时代&#xff0c;智慧营销被誉为企业提升竞争力的关键。而EOS智慧营销系统作为一款领先于时代潮流的创新软件&#xff0c;正以其卓越的功能和不可替代的优势&#xff0c;引领着营销智能化的新时代。 EOS智慧营销系统的独特之处在于其全面而细致的市场分析能力。通过大数…

印染工厂5G智能制造数字孪生可视化平台,推进行业数字化转型

印染工厂5G智能制造数字孪生可视化平台&#xff0c;推进行业数字化转型。印染工厂正迈入一个全新的时代&#xff0c;这个时代以5G智能制造数字孪生可视化平台为核心&#xff0c;推动整个行业的数字化转型。不仅是一场技术革命&#xff0c;更是一次产业变革&#xff0c;为印染工…

IP SSL证书申请教程:实现HTTPS加密访问

随着网络安全意识的提高&#xff0c;HTTPS加密访问已经成为网站安全性的重要标准。通过安装SSL证书&#xff0c;网站可以实现数据的加密传输&#xff0c;有效保护用户隐私和数据安全。本文将详细介绍如何为IP地址申请SSL证书&#xff0c;并实现HTTPS加密访问。 一、准备工作 …

常用目标检测算法介绍

目录 1. 常用目标检测算法 2. R-CNN 模型 3. Fast R-CNN 模型 4. Faster R-CNN 模型 5. SSD 模型 1. 常用目标检测算法 在深度学习框架下&#xff0c;目标检测方法通常涉及图像定位和分类两个关键方面。有两种主要的解决方法&#xff1a;一种是一阶&#xff08;one-stage&…

【JAVA进阶篇教学】第十一篇:Java中ReentrantLock锁讲解

博主打算从0-1讲解下java进阶篇教学&#xff0c;今天教学第十篇&#xff1a;Java中ReentrantLock锁讲解。 在Java并发编程中&#xff0c;保证多线程环境下的数据安全是至关重要的。ReentrantLock 是Java中用于实现线程安全的一种锁机制。本篇博客将深入介绍 ReentrantLock 的原…

商场超市会员日活动怎么群发短信营销

商场超市会员日活动怎么群发短信营销 短信营销的优势 短信营销是一种直接有效的市场推广方式&#xff0c;它具有以下优势&#xff1a; 高开放率&#xff1a;相比于其他营销方式&#xff0c;具有较高的开放率&#xff0c;因为手机用户几乎每天都会查看。 即时性&#xff1a;群…

[单片机课设]十字路口交通灯的设计

题目要求&#xff1a; 模拟交通灯运行情况。南北绿灯亮30秒&#xff0c;南北黄灯亮3秒&#xff0c;东西红灯亮33秒&#xff1b;南北红灯亮33秒&#xff0c;东西绿灯亮30秒&#xff0c;东西黄灯亮3秒&#xff1b;要求数码管同步显示时间的倒计时&#xff0c;用定时器实现延时。…

DELL EMC unity存储系统如何初始化

在客户的存储使用过程中&#xff0c;经常会碰到一些场景需要对存储系统做重新初始化&#xff0c;就是回到出厂时候的配置。比如&#xff0c;客户设备要利旧&#xff0c;二次使用&#xff0c;一般都要回到出厂状态做重新配置的动作。存储严重故障&#xff0c;没有能力修复或者数…