解析Top-K问题及堆排序算法

Top-K问题是在海量数据中找到最大或最小的K个元素,它在实际应用中非常常见,例如专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。在面对大规模数据时,直接对数据进行排序可能效率低下,因为排序的时间复杂度通常为O(n log n),而海量数据可能无法完全加载到内存中。因此,我们需要一种更高效的算法来解决Top-K问题

 

用堆解决Top-K问题

堆排序是一种高效的解决Top-K问题的方法。基本思路如下:

  1. 用数据集合中前K个元素来建堆。对于前K个最大的元素,我们建立一个小堆;对于前K个最小的元素,我们建立一个大堆。

  2. 用剩余的N-K个元素依次与堆顶元素来比较,如果大于(或小于)堆顶元素,则替换堆顶元素,并进行堆调整。

通过这个过程,堆中剩余的K个元素就是所求的前K个最小或最大的元素

void PrintTopK(int* a, int n, int k)
{// 1. 建堆--用a中前k个元素建堆// 2. 将剩余n-k个元素依次与堆顶元素交换,不满则则替换
}
void TestTopk()
{int n = 10000;int* a = (int*)malloc(sizeof(int)*n);srand(time(0));for (size_t i = 0; i < n; ++i){a[i] = rand() % 1000000;}a[5] = 1000000 + 1;a[1231] = 1000000 + 2;a[531] = 1000000 + 3;a[5121] = 1000000 + 4;a[115] = 1000000 + 5;a[2335] = 1000000 + 6;a[9999] = 1000000 + 7;a[76] = 1000000 + 8;a[423] = 1000000 + 9;a[3144] = 1000000 + 10;PrintTopK(a, n, 10);
}

对随机位置改10个值  如果能选出这10个 就说明代码没问题

假设有10亿个值  这时让你取前10 那我们选择建立一个10个数据大小的堆

我们读写整形用fscanf   和fprintf

这时我们创建一个函数将其写入文档

void CreateNode()
{int n = 10000000;srand(time(0));const char* file = "data.txt";FILE* fin= fopen(file, "w");if (fin == NULL){perror("fopen fail");return;}for (int i = 0; i < n; ++i){int x = (rand() + i) % 10000000;//+i是为了减少重复值 因为rand最多就三万个随机值 fprintf(fin, "%d\n", x);}
}

 

此时文件已经被创建成功 

 

接下来实现打印Top k

 在这里我们创建了一个小堆 由于fscanf的返回值是当读取结束时返回EOF

 所以我们可以创造循环

 然后我们填写测试用例

通过调试我们可以看到他的逻辑过程 

 最后得出结果

实现代码

以下是一个简单的C语言代码示例,展示了如何使用小堆解决Top-K问题:

 

#include <stdio.h>
#include <stdlib.h>
#include <time.h>// 堆数据结构
typedef struct {int* a;         // 存放堆元素的数组int capacity;   // 数组容量int size;       // 当前堆的大小
} HP;// 交换两个元素的值
void Swap(int* p1, int* p2) {int tmp = *p1;*p1 = *p2;*p2 = tmp;
}// 向上调整(建堆时使用)
void Adjustup(int* a, int child) {int parent = (child - 1) / 2;while (child > 0 && a[child] < a[parent]) {Swap(&a[child], &a[parent]);child = parent;parent = (parent - 1) / 2;}
}// 向下调整(堆调整时使用)
void Adjustdown(int* a, int size, int parent) {int child = parent * 2 + 1;while (child < size) {if (child + 1 < size && a[child + 1] < a[child]) {++child;}if (a[child] < a[parent]) {Swap(&a[child], &a[parent]);parent = child;child = (child + 1) * 2;} else {break;}}
}// 初始化堆
void HPInit(HP* php, int capacity) {php->a = (int*)malloc(sizeof(int) * capacity);php->capacity = capacity;php->size = 0;
}// 销毁堆
void HPDestory(HP* php) {free(php->a);php->a = NULL;php->capacity = 0;php->size = 0;
}// 入堆操作
void HPPush(HP* php, int x) {if (php->size == php->capacity) {// 堆满时,扩容int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;int* tmp = (int*)realloc(php->a, sizeof(int) * newcapacity);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);
}// 出堆操作
int HPPop(HP* php) {if (php->size == 0) {perror("Heap is empty");return -1;  // 堆为空}int top = php->a[0];php->a[0] = php->a[php->size - 1];php->size--;Adjustdown(php->a, php->size, 0);return top;
}// 获取堆顶元素
int HPTop(const HP* php) {if (php->size == 0) {perror("Heap is empty");return -1;  // 堆为空}return php->a[0];
}// 打印前K个最小元素
void PrintTopK(const char* file, int k) {FILE* fin = fopen(file, "r");if (fin == NULL) {perror("fopen fail");return;}// 建立一个大小为k的小堆HP minheap;HPInit(&minheap, k);int x = 0;// 读取文件中的元素并插入堆while (fscanf(fin, "%d", &x) != EOF) {if (minheap.size < k) {// 如果堆的大小小于k,直接插入HPPush(&minheap, x);} else if (x > HPTop(&minheap)) {// 否则,如果当前元素比堆顶元素大,替换堆顶元素并调整堆HPPop(&minheap);HPPush(&minheap, x);}}// 输出结果for (int i = 0; i < k; i++) {printf("%d ", HPPop(&minheap));}printf("\n");fclose(fin);HPDestory(&minheap);
}// 生成随机数据文件
void CreateNode() {int n = 10000000;srand(time(0));const char* file = "data.txt";FILE* fin = fopen(file, "w");if (fin == NULL) {perror("fopen fail");return;}for (int i = 0; i < n; ++i) {int x = (rand() + i) % 10000000;fprintf(fin, "%d\n", x);}fclose

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言进阶指南(14)(部分字符串库函数及其模拟实现)

欢迎来到博主的专栏——C语言进阶指南 博主id&#xff1a;reverie_ly 文章目录 1、strlen&#xff08;&#xff09;——字符串长度计算函数自定义strlen函数的实现 2、strcpy——字符串拷贝函数strcpy的模拟实现 3.strcat——字符串追加函数strcat的模拟实现 4、strcmp——字符…

【Python】yaml.safe_load()函数详解和示例

在Python中&#xff0c;PyYAML库提供了对YAML&#xff08;YAML Ain’t Markup Language&#xff09;文件的强大支持。YAML是一种直观的数据序列化标准&#xff0c;可以方便地存储和加载配置文件、数据日志等。 yaml.safe_load和yaml.load是Python的PyYAML库提供的两个函数&…

边界突破之linux系统上线Cobalt Strike

别低头&#xff0c;皇冠会掉&#xff1b;别流泪&#xff0c;坏人会笑 基础文件 加载插件 服务端开启监听 windows/beacon_https/reverse_https 类型的beacon 生成木马Beacon 命令如下 linux ./genCrossC2.Linux [TeamServer的IP] [HTTPS监听器端口] [.cobaltstrike.beacon_k…

【洛谷算法题】P5716-月份天数【入门2分支结构】

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5716-月份天数【入门2分支结构】&#x1f30f;题目描述&#x1f30f;输入格式&a…

(C++)移动零--双指针法

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名企 Dream Offer。https://le…

IDEA如何配置Git 遇到问题的解决

新建项目 点击 会变红 会生成.git隐藏文件 配置远程仓库路径&#xff1a;点击Manage Remotes&#xff1a;将远程仓库的链接放到这里&#xff1a; 得到如下样式&#xff1a; 此时提交到本地仓库 点击add&#xff0c;添加到暂存文件&#xff1a; 此时文件变绿&#xf…

J2EE征程——第一个纯servletCURD

第一个纯servletCURD 前言在此之前 一&#xff0c;概述二、CURD1介绍2查询并列表显示准备实体类country编写 CountryListServlet配置web.xml为web应用导入mysql-jdbc的jar包 3增加准备增加的页面addc.html编写 CAddServlet配置web.xml测试 4删除修改CountryListServlet&#xf…

36.JavaScript补完计划:typescript

点赞收藏加关注&#xff0c;你也能住大别墅&#xff01; 一、什么是typescript 二、应用场景 我认为JavaScript的特点就是在于它强大的延展性&#xff0c;不仅蔓延到了后端&#xff0c;而且也逐渐成为代码世界无法被忽视的存在。那么&#xff0c;编写js代码时我们都会经常遇到…

【Springboot系列】SpringBoot整合Jpa

文章目录 前言&#xff1a;什么是JPA&#xff1f;JPA优缺点优点1.简化开发&#xff1a;2.高度抽象&#xff1a;3.跨数据库支持&#xff1a;4.自动化的事务管理&#xff1a; 缺点1.学习成本较高&#xff1a;2.性能问题&#xff1a;3.灵活性受限&#xff1a; 示例版本依赖代码Use…

vuepress-----7、发布在GitHub

# 7、发布在GitHub 在你的项目中&#xff0c;创建一个如下的 deploy.sh 文件&#xff08;请自行判断去掉高亮行的注释&#xff09;: #!/usr/bin/env sh# 确保脚本抛出遇到的错误 set -e# 生成静态文件 npm run docs:build# 进入生成的文件夹 cd docs/.vuepress/dist# 如果是发…

7.浮点数转为整数【2023.11.29】

1.问题描述 给出一个浮点数&#xff0c;请将这个浮点数转换成整数。 2.解决思路 输入一个浮点数。 输出程序将浮点数转换为整数并输出。 3.代码实现 numfloat(input("请输入一个浮点数")) num1int(num) print(num1)4.运行结果

RabbitMQ的Web管理页面

访问页面 http://IP:15672/账号密码默认都是&#xff1a;guest 主页概览 Overview 显示当前RabbitMQ Broker的运行信息、连接信息、集群信息以及配置信息等。 连接 Connections 无论生产者还是消费者&#xff0c;都需要与RabbitMQ建立连接后才可以完成消息的生产和消费&#…