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

news2024/11/20 3:38:07
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.coloradmin.cn/o/1269627.html

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

相关文章

Notepad++批量添加引号

工作中经常会遇到这样情景&#xff1a;业务给到一批订单号&#xff0c;需要查询这批订单的某些字段信息。在where条件中需要传入这些订单号的数组&#xff0c;并且订单号用引号引起&#xff0c;用引号隔开。 字符串之间长度相同 可以按住CtrlAlt和鼠标左键选中区域&#xff0…

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

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

力扣日记11.28-【二叉树篇】二叉树的最小深度

力扣日记&#xff1a;【二叉树篇】二叉树的最小深度 日期&#xff1a;2023.11.28 参考&#xff1a;代码随想录、力扣 111. 二叉树的最小深度 题目描述 难度&#xff1a;简单 给定一个二叉树&#xff0c;找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点…

Gitee-PicGo-Typora

Gitee-PicGo-Typora 问题引出 问题1&#xff1a;根据相关法律法规和政策&#xff0c;您的部分文件因存在敏感信息而无法显示 就在昨晚&#xff0c; 我在记笔记的时候&#xff0c;发现之前配置的七牛云图床出了问题&#xff1a; 1、根据相关法律法规和政策&#xff0c;您的部…

联邦学习(Federated Learning)

联邦学习&#xff08;Federated Learning&#xff09;是一种保护用户隐私的分布式机器学习方法&#xff0c;在联邦学习中&#xff0c;模型的训练是在分布式的客户端设备上进行的&#xff0c;而模型的更新则是在中央服务器上进行的。联邦学习的目标是通过共享模型而不是原始数据…

C# 使用 Fody 监控方法执行时间

写在前面 在做性能调优的时候&#xff0c;经常需要跟踪具体方法的执行时间&#xff1b;通过插入Stopwatch的方案对代码的侵入性太高了&#xff0c;所以引入了 MethodTimer.Fody 类库&#xff0c;采用编译时注入的方式给方法动态加上Stopwatch 跟踪代码&#xff0c;只需要在目标…

探索低代码之路——JNPF

目录 一、低代码行业现状 二、产品分析 1.可视化应用开发 2.流程管理 3.整个平台源码合作 三、架构和技术 技术栈 四、规划和展望 低代码平台&#xff08;Low-code Development Platform&#xff09;是一种让开发者通过拖拽和配置&#xff0c;而非传统的手动编写大量代…

geoserver根据数据字段动态设置样式

一、数据展示&#xff1a; 二、样式设置 <?xml version"1.0" encoding"UTF-8"?> <StyledLayerDescriptor version"1.0.0" xsi:schemaLocation"http://www.opengis.net/sld StyledLayerDescriptor.xsd" xmlns"http://…

从0开始学习JavaScript--JavaScript 模板字符串的全面应用

JavaScript 模板字符串是 ES6 引入的一项强大特性&#xff0c;它提供了一种更优雅、更灵活的字符串拼接方式。在本文中&#xff0c;将深入探讨模板字符串的基本语法、高级用法以及在实际项目中的广泛应用&#xff0c;通过丰富的示例代码带你领略模板字符串的魅力。 模板字符串…

Efficient RGB-T Tracking via Cross-Modality Distillation

摘要 目前大多数RGB-T跟踪器采用双流结构来提取单个RGB和热红外特征&#xff0c;并采用复杂的融合策略来实现多模态特征融合&#xff0c;这需要大量的参数&#xff0c;阻碍了它们的实际应用。另一方面&#xff0c;一个紧凑的RGB-T跟踪器可能具有计算效率&#xff0c;但由于特征…

01数仓平台 Hadoop介绍与安装

Hadoop概述 Hadoop 是数仓平台的核心组件。 在 Hadoop1.x 时代&#xff0c;Hadoop 中的 MapReduce 同时处理业务逻辑运算和资源调度&#xff0c;耦合性较大。在 Hadoop2.x 时代&#xff0c;增加了 Yarn。Yarn 只负责资源的调度&#xff0c;MapReduce 只负责运算。Hadoop3.x 在…

开源好用EasyImages简单图床源码

开源好用EasyImages简单图床源码分享&#xff0c;虽然它是开源程序&#xff0c;但功能一点也不弱&#xff0c;不仅支持多文件上传、文字/图片水印、支持API和鉴黄、还能自定义代码&#xff0c;最重要的是它不强制使用数据库运行&#xff0c;这就给我们的部署和维护带来极大方便…

GDPU 数据结构 天码行空12

文章目录 数据结构实验十二 图的遍历及应用一、【实验目的】二、【实验内容】三、实验源代码&#x1f37b; CPP&#x1f37b; C 数据结构实验十二 图的遍历及应用 一、【实验目的】 1、 理解图的存储结构与基本操作&#xff1b; 2、熟悉图的深度度优先遍历和广度优先遍历算法…

RabbitMQ快速学习之WorkQueues模型、三种交换机、消息转换器(基于SpringBoot)

文章目录 前言一、WorkQueues模型消息发送消息接收能者多劳 二、交换机类型1.Fanout交换机消息发送消息接收 2.Direct交换机消息接收消息发送 3.Topic交换机消息发送消息接收 三、编程式声明队列和交换机fanout示例direct示例基于注解 四、消息转换器总结 前言 WorkQueues模型…

借助 DPM 代码扫描的力量解锁医疗设备的可追溯性

在当今的医疗保健系统中&#xff0c;医疗设备的可追溯性变得比以往任何时候都更加重要。为了增强现代医疗保健领域的可追溯性和安全性&#xff0c;UDI 条形码充当唯一设备标识的标准&#xff0c;为医疗设备提供唯一标识符。 DataMatrix 代码&#xff08;或直接零件标记代码&am…

矩阵代数与MATLAB实现(特征值、广义特征值、酋矩阵、)

矩阵代数的相关知识 目录 一、特征值与特征向量 1、特征值与特征向量 2、MATLAB计算 二、广义特征值与广义特征向量 1、广义特征值与广义特征向量 2、MATLAB计算 三、酋矩阵 1、酋矩阵 2、MATLAB计算 四、未完待续 总结 提示&#xff1a;以下是本篇文章正文内容&…

动态规划:解决复杂问题的利器(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

Android控件全解手册 - 多语言切换完美解决方案(兼容7.0以上版本)

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…

深入理解OS--硬件高速缓存,缓存一致性,存储设备

存储技术 1.SRAM&#xff0c;DRAM 静态&#xff1a;更快&#xff0c;用作高速缓存存储器。处理器高速缓存采用SRAM。 动态&#xff1a;用作主存及图形系统的帧缓冲区。内存采用DRAM。 2.内存 2.1.内存数据访问示例 设备控制器存在缓存。设备芯片自身存在缓存。 2.2.采用并行…

Spring不再支持Java8了

在今天新建模块的时候发现了没有java8的选项了&#xff0c;结果一查发现在11月24日&#xff0c;Spring不再支持8了&#xff0c;这可怎么办呢&#xff1f;我们可以设置来源为阿里云https://start.aliyun.com/ 。 java8没了 设置URL为阿里云的地址