海量数据中找出前k大数(topk问题),一篇文章教会你

news/2024/10/20 3:51:09/

💯 博客内容:【数据结构】向上调整建堆和向下调整建堆的天壤之别以及堆排序算法

😀 作  者:陈大大陈

🚀 个人简介:一个正在努力学技术的准前端,专注基础和实战分享 ,欢迎私信!

💖 欢迎大家:这里是CSDN,我总结知识和写笔记的地方,喜欢的话请三连,有问题请私信 😘 😘 😘

目录

什么是TOPK问题?

TOPK问题的必要性和详细思路 

 包含TOPK算法的堆的源代码 

TOPK算法复杂度分析 

什么是TOPK问题?

📝TOP-K问题:在数据量比较大的情况下求数据集合中前K个最大的元素或者最小的元素,

比如:世界500强的企业、世界富豪榜、游戏中前100的氪金玩家等。

TOPK问题的必要性和详细思路 

N个数寻找最大的前k个。

正常思路是把这N个数建成堆,然后 pop k次,即可找到最大的前k个值。

但是有些场景,上面的思路解决不了,比如N非常大的情况。

假设N是10亿,k是100,就不可能运行出结果。

因为如果这样建成堆的话,所占用的空间太大了。

10亿个整数需要多大的空间?答案是4GB。

4GB的内存啥电脑来了也吃不消,再说100亿,1000亿呢?

可见这不是一个可行的方法,咱们另辟蹊径。

我们知道,当数据很大时,它不会存在内存上,而是转而储存在磁盘文件里,所以我们的思路就转向磁盘文件。

磁盘中能建堆吗?答案是不能,因为磁盘不能随机访问。

解决思路,前K个数建小堆,让其它N-K个数和堆顶比较,如果比它大就替换它进堆。

替换它进堆的意思就是,将堆顶元素替换,然后将其向下调整。

最后这个小堆的内容就是最大的前K个。

向下调整算法我上篇文章介绍过,大家不了解的可以看看。

(4条消息) 【数据结构】向上调整建堆和向下调整建堆的天壤之别以及堆排序算法_陈大大陈的博客-CSDN博客

考虑到要排序的数据可能很多,我们建立一个TXT文件来存放待排序数据。

为了创建足够多的随机数,我们使用srand函数。

void CreateNDate()
{// 造数据int n = 1000;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);
}

 关于文件的操作,大家有忘记的可以看我之前的博客。

写程序必会的C语言文件操作(上)附手绘图详解_陈大大陈的博客-CSDN博客

运行代码。

上面我们创建了一个data文件,并且往里面存入了1000个随机数。 

 用上面的思路将其排序。

首先是建堆,用a中前k个元素建小堆。

然后将剩余n-k个元素依次与堆顶元素交换,不满则则替换。

替换元素之后不要忘记向下调整。

void PrintTopK(int k)
{// 1. 建堆--用a中前k个元素建小堆int* topk = (int*)malloc(sizeof(int) * k);assert(topk);const char* file = "data.txt";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);}

我们人工在data文件里面建立几个大数,运行代码。

 成功打印出我们添加的几个大数,排序成功 。

 包含TOPK算法的堆的源代码 

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
#include<time.h>
typedef int HPDataType;
typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;
void AdjustUp(HPDataType* a, int child);
void AdjustDown(HPDataType* a, int n, int parent);
void HeapInit(HP* php);
void HeapDestroy(HP* php);
void HeapPush(HP* php, HPDataType x);
void HeapPop(HP* php);
HPDataType HeapTop(HP* php);
bool HeapEmpty(HP* php);
//int HeapSize(HP* php);
void Swap(HPDataType* p1, HPDataType* p2);
void HeapSort(int* a, int n);
void HeapDestroy(HP* php);
void CreateNDate();
void PrintTopK(int k); 
#define _CRT_SECURE_NO_WARNINGS
#include"heap.h"
void HeapInit(HP* php)
{assert(php);php->a = NULL;php->size = 0;php->capacity = 0;
}void HeapDestroy(HP* php)
{assert(php);free(php->a);php->a = NULL;php->capacity = 0;
}void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}
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 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;}}
}
bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}
void HeapPush(HP* php, HPDataType x)
{assert(php);if (php->size == php->capacity){int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->a, newCapacity * sizeof(HPDataType));if (tmp == NULL){perror("realloc fail");return;}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size - 1);
}
void HeapPop(HP* php)
{assert(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];
}//void HeapSort(int* a, int n)
//{
//	HP hp;
//	HeapInit(&hp);
//	for (int i = 0; i < n; i++)
//	{
//		HeapPush(&hp, a[i]);
//	}
//	int i = 0;
//	while (!HeapEmpty(&hp))
//	{
//		int top = HeapTop(&hp);
//		a[i++] = top;
//		HeapPop(&hp);
//	}
//	HeapDestroy(&hp);
//}
//换出我们想要的结构,向上建立小堆,然后删除元素,最后建立大堆 1 3 5 4 5 9 7 8
//建小队回损坏结构
//向下调整算法可以帮我们完成工作
//升序-》建大堆    降序-》建小堆void HeapSort(int* a, int n)
{int end = n-1;//向上调整建堆for (int i = 1; i < n; i++){AdjustUp(a, i);}//向下调整建堆for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(a, n, i);}while (end>0){Swap(&a[0], &a[end]);AdjustDown(a,end, 0);--end;}
}
void CreateNDate()
{// 造数据int n = 1000;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);
}
void PrintTopK(int k)
{// 1. 建堆--用a中前k个元素建小堆int* topk = (int*)malloc(sizeof(int) * k);assert(topk);const char* file = "data.txt";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);}
//test.c
#define _CRT_SECURE_NO_WARNINGS
#include"heap.h"
void UpSort(HPDataType* a, int n)
{for (int i = 1; i < n; i++){AdjustUp(a, i);}
}
int main()
{HP hp;HeapInit(&hp);int a[] = { 7,8,4,6,3,0,1,2,5,9 };//HeapSort(a, sizeof(a) / sizeof(a[0]));int n = sizeof(a) / sizeof(int);HeapSort(a, sizeof(a) / sizeof(a[0]));/*for (int i = 0; i < sizeof(a) / sizeof(int); ++i){HeapPush(&hp, a[i]);}while (!HeapEmpty(&hp)){printf("%d\n", HeapTop(&hp));HeapPop(&hp);}*/for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++){printf("%d ", a[i]);}printf("\n");//CreateNDate();PrintTopK(5);return 0;
}

TOPK算法复杂度分析 

​ 一开始需要对K个元素进行建堆,时间复杂度为O(k),之后遍历数组,最坏的情况是,每个元素都与堆顶比较并排序,需要调整n次 复杂度是O(nlog(k)),因此总复杂度是O(k+nlog(k))

如有错误欢迎大佬评论区指正。

欢迎转载,烦请署名并保留原文链接。


http://www.ppmy.cn/news/96632.html

相关文章

HNU-电路与电子学-综合设计-模型机

写在前面&#xff1a; 每一年的综合设计肯定都有改动&#xff0c;故肯定不能照搬照抄&#xff0c;大家可以借鉴思路&#xff0c;复现成果&#xff0c;但要注意不同之处&#xff0c;以免被0分处理。 模型设计的指导书我不放在这里了&#xff0c;因为那是课程组老师们的成果。 …

Ping过程 原理 详解

Ping过程 原理 详解 ***\*3:\**表示RARP请求** ***\*4:\**表示RARP应答** 再继续… 如果主机A要ping主机C,那么主机A发现主机C的IP和自己不是同一网段,他就去找 ***\*网关\**** 转发,但是他也不知道网关的MAC情况下呢?他就会向之前那个步骤一样 **先发送一个 ARP广播…

SAP-MM费用类采购通过物料组确定科目

一、WRX的配置&#xff0c;分两类GR/IR科目&#xff1a; 1、做库存管理物料的GR/IR科目&#xff0c;需要配置评估类&#xff0c;此评估类就是物料主数据里配置的评估类&#xff1b; 2、非库存管理费用化物料的GR/IR科目&#xff0c;如固定资产、办公用品、低值易耗品等等&#…

堆(堆排序 模拟堆)

目录 一、堆的数据结构二、堆的操作方法往下调整的示意图往上调整的示意图相关功能的实现思路1.插入一个数2.求最小值3.删除最小值4.删除任意一个元素5.修改任意一个元素 三、堆的实战运用堆排序模拟堆 一、堆的数据结构 堆是一个完全二叉树&#xff1a;除了最后一层结点以外&…

Office project 2010安装教程

哈喽&#xff0c;大家好。今天一起学习的是project 2010的安装&#xff0c;Microsoft Office project项目管理工具软件&#xff0c;凝集了许多成熟的项目管理现代理论和方法&#xff0c;可以帮助项目管理者实现时间、资源、成本计划、控制。有兴趣的小伙伴也可以来一起试试手。…

全网最全最有用的网络安全学习路线!整整一晚上才整理出来!

正文&#xff1a; 废话不多说&#xff0c;先上一张图镇楼&#xff0c;看看网络安全有哪些方向&#xff0c;它们之间有什么关系和区别&#xff0c;各自需要学习哪些东西。 在这个圈子技术门类中&#xff0c;工作岗位主要有以下三个方向&#xff1a; 安全研发安全研究&#xff1…

openGauss 3.1企业版升级至5.0

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

网络安全术语

网络安全术语 AC Access Controller&#xff0c;接入控制器 AP Access Point&#xff0c;接入点 ASG Application Security Gateway&#xff0c;应用安全网关 BFD Bidirectional Forwarding Detection&#xff0c;双向转发检测 BGP Border Gateway Protocol&#xff0c;边…