堆的应用2——TOPK问题

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.mzph.cn/news/834174.shtml

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

相关文章

JSONArray怎么通过jsonObject中的某个属性值进行分组

Java JSONArray怎么通过jsonObject中的某个属性值进行分组 在Java中&#xff0c;可以使用JSONArray和JSONObject来处理JSON数据。如果你想要通过jsonObject中的某个属性值对JSONArray进行分组&#xff0c;可以使用Java 8的Stream API来简化操作。 以下是一个简单的例子&#…

嵌入式C语言高级教程:实现基于STM32的自适应交通信号控制系统

自适应交通信号控制系统能够基于实时交通流数据调整信号灯的时长&#xff0c;提高路口的通行效率。本教程将指导您如何在STM32微控制器上实现一个基本的自适应交通信号控制系统。 一、开发环境准备 硬件要求 微控制器&#xff1a;STM32F103C8&#xff0c;具备足够的处理能力…

Eclipse下载安装教程(包含JDK安装)【保姆级教学】【2023.10月最新版】

目录 文章最后附下载链接 第一步&#xff1a;下载Eclipse&#xff0c;并安装 第二步&#xff1a;下载JDK&#xff0c;并安装 第三步&#xff1a;Java运行环境配置 安装Eclipse必须同时安装JDK &#xff01;&#xff01;&#xff01; 文章最后附下载链接 第一步&#xf…

使用Python和MoviePy库实现视频拼接与合成的技巧与实践

首先&#xff0c;我们需要导入所需的库&#xff1a; from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeVideoClip, ColorClip import os import random from typing import List from enum import Enum 接下来&#xff0c;我们定义…

Git如何查看有多少个分支、创建分支、切换分支

2024年5月9日&#xff0c;周四上午 在 Git 中&#xff0c;新建一个分支是非常简单的。以下是如何操作的步骤&#xff1a; 查看现有分支&#xff1a;git branch这个命令会列出当前仓库中的所有本地分支。创建新分支&#xff1a;git branch <new-branch-name>这个命令会创…

[法规规划|数据概念]金融行业数据资产和安全管理系列文件解析(3)

“ 金融行业在自身数据治理和资产化建设方面一直走在前列。” 一直以来&#xff0c;金融行业由于其自身需要&#xff0c;都是国内开展信息化建设最早&#xff0c;信息化程度最高的行业。 在当今数据要素资产化的浪潮下&#xff0c;除了行业自身自身数据治理和资产化建设方面&am…

EditReady for Mac激活版:专业视频转码工具

对于视频专业人员来说&#xff0c;一款高效的视频转码工具是不可或缺的。EditReady for Mac正是这样一款强大的工具&#xff0c;它拥有简洁直观的操作界面和强大的功能&#xff0c;让您的视频处理工作事半功倍。 EditReady for Mac支持多种视频格式的转码&#xff0c;并且支持常…

【AIGC调研系列】红帽的RHEL AI能够做什么

红帽的RHEL AI&#xff08;Red Hat Enterprise Linux AI&#xff09;是一个基于开源项目InstructLab和IBM Research提供的Granite大型语言模型的基础模型平台。它旨在无缝开发、测试和运行生成式AI模型&#xff0c;以支持企业应用程序[1][2][5]。 具体来说&#xff0c;RHEL AI…

Java护照识别接口开发示例、文字识别、证件识别

护照是我们出国旅行时所必要的证件之一&#xff0c;他是我国公民去外国的旅行和工作的时候所代表的一个合法的身份证件。 在护照上面也有不少关于我们个人身份的信息&#xff0c;而手动去录入如此多的身份信息这绝对是灾难。不仅证件&#xff0c;有的场景还需要录入很多文字信息…

React 之 记忆化函数cache(十四)

const result cache&#xff08;fn&#xff09;方法&#xff1a; fn&#xff1a;要对其结果进行缓存的函数。fn 可以接受任何参数并返回任何值。 返回值result : 返回一个与 fn 具有相同类型签名的已缓存版本。在此过程中&#xff0c;它不会调用 fn。 cache 用法 import {cac…

【Java】初识网络编程

文章目录 前言✍一、互联网的发展1.独立模式2.网络的出现局域网LAN广域网WAN ✍二、网络编程概述✍三、网络编程中的术语介绍IP地址端口号协议OSI七层模型TCP\IP四层模型 ✍四、协议的层级之间是如何配合工作的 前言 在本文中&#xff0c;会对网络编程的一些术语进行解释&#…

动态规划——路径问题:931.下降路径最小和

文章目录 题目描述算法原理1.状态表示&#xff08;经验题目&#xff09;2.状态转移方程3.初始化4.填表顺序5.返回值 代码实现CJava 题目描述 题目链接&#xff1a;931.下降路径最小和 关于这⼀类题&#xff0c;看过我之前的博客的朋友对于状态表示以及状态转移是⽐较容易分析…

5分钟了解下HDFS

随着大数据时代的到来&#xff0c;传统的数据存储和管理方式已经无法满足日益增长的数据处理需求。HDFS&#xff08;Hadoop Distributed File System&#xff09;作为Apache Hadoop项目的一部分&#xff0c;以其高度的容错性、可扩展性和高吞吐量&#xff0c;成为了处理大规模数…

抖音APP运用的AI技术拆解

1.推荐系统&#xff08;RS&#xff09; 用户画像&#xff1a;根据用户的信息&#xff08;如地区、性别、年龄、收藏、关注......&#xff09;进行分析&#xff0c;构建用户画像&#xff0c;对用户进行分类&#xff1b; 行为分析&#xff1a;将用户的显形行为数据&#xff08;如…

搜维尔科技:OptiTrack是基于LED墙虚拟制作舞台的最佳选择

OptiTrack因其绝对精度、易用性、可靠性以及与现场工具的完美集成而被选中&#xff0c;仍然是全球首屈一指的基于 LED 墙的虚拟制作舞台的选择。 当今虚拟制作阶段的低延迟、超精确摄像机跟踪标准 /- 0.2 毫米 位置精度1 < 10 毫秒 系统延迟 /- 0.1 度 旋转精度2 电影…

Linux 操作系统网络编程1

目录 1、网络编程 1.1 OSI 网络七层模型 1.1.1 OSI 参考模型 1.1.2 网络数据传输过程 2 传输层通信协议 2.1 TCP 2.1.1 TCP的3次握手过程 2.1.2 TCP四次挥手过程 2.2 UDP 3 网络编程的IP地址 4 端口 5 套接字 1、网络编程 1.1 OSI 网络七层模型 1.1.1 OSI 参考模型…

vue根据文字动态判断溢出...鼠标悬停显示el-tooltip展示

使用自定义el- tooltip 组件 定义 Tooltip是一种小型弹出框&#xff0c;它显示有关特定页面元素的信息&#xff0c;例如按钮、链接或图标。Tooltip通常以半透明的气泡形式呈现&#xff0c;并出现在页面元素的旁边或下方。 它可以改善用户体验&#xff0c;使用户更容易理解页面…

Linux cmake 初窥【3】

1.开发背景 基于上一篇的基础上&#xff0c;已经实现了多个源文件路径调用&#xff0c;但是没有库的实现 2.开发需求 基于 cmake 的动态库和静态库的调用 3.开发环境 ubuntu 20.04 cmake-3.23.1 4.实现步骤 4.1 准备源码文件 基于上个试验的基础上&#xff0c;增加了动态库…

优思学院|精益六西格玛黑带培训的内容有哪些?【附思维导图】

引言 在现代企业中&#xff0c;精益六西格玛黑带&#xff08;或称六西格玛黑带&#xff09;处于实施六西格玛方法的最前线。然而&#xff0c;他们的职责远不止执行者的角色。黑带既学习六西格玛的工具&#xff0c;又主导项目以改进绩效指标。在更长远的角度看&#xff0c;他们…

擎天科技与禅道合作,打造统一的项目管理平台

统一、全面的项目管理平台能够帮助企业优化管理流程&#xff0c;提升业务效率。擎天集团选择与禅道软件合作&#xff0c;打造统一的项目管理平台&#xff0c;在降低自研软件的研发成本、打破团队信息孤岛、保障数据全面性等方面效果显著&#xff0c;大大提高了团队沟通协作效率…