【数据结构】堆的应用-----TopK问题

目录

一、前言

二、Top-k问题 

 💦解法一:暴力排序

💦解法二:建立N个数的堆

💦解法三:建立K个数的堆(最优解)

三、完整代码和视图 

四、共勉


一、前言

在之前的文章中,已经详细的讲解了二叉树、堆、堆排序。那么关于堆还有一个比较有意思的题,就是TopK问题。

如果对堆和二叉树还不够了解的可以看看我之前的文章哦!!!

详解二叉树和堆

二、Top-k问题 

Top-k问题:在 N 个数中,找出前 K 个(最大/最小)的元素,一般情况下数据量 N 都远大于 k。

Top-k问题在生活中是非常的常见,比如游戏中某个大区某个英雄熟练度最高的前10个玩家的排名,我们就要根据每个玩家对该英雄的熟练度进行排序,可能有200万个玩家,但我只想选出前10个,要对所有人去排个序吗?显然没这个必要。

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

 💦解法一:暴力排序

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

我们用堆排序,其时间复杂度为:O(N*log2N)。

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


💦解法二:建立N个数的堆

建一个 N 个数的堆(C++中可用优先级队列priority_queue),不断的选数,选出前 k 个。

时间复杂度:建N个数的堆为O(N),获取堆顶元素 (也即是最值) 并删除掉堆顶元素为O(log2N),上述操作重复 k 次,所以时间复杂度为O(N+k*log2N)。

【思考】

能否再优化一下呢?假设 N 是 10 亿数,内存中放不下,是放在文件中的。前面两个方法都不能用了。


💦解法三:建立K个数的堆(最优解)

✨基本思想:

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

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

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

用剩余的 N-K 个元素依次与堆顶元素来比较,不满足则删除堆顶元素,再插入。

找前 k 个最大的元素,大于堆顶元素,则删除堆顶元素,再插入

找前 k 个最小的元素,小于堆顶元素,则删除堆顶元素,再插入

将剩余的 N-K 个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。


✨时间复杂度:

▶ 建 k 个元素的堆为O(K);
▶ 遍历剩余的 N-K 个元素的时间代价为O(N-K),假设运气很差,每次遍历都入堆调整;
▶ 入堆调整:删除堆顶元素和插入元素都为O(log2K);
▶ 所以时间复杂度为O(k + (N-K)log2K)。当 N 远大于 K 时,为O(N*log2K),这种解法更优。

 

✨假如要找出最大的前 10 个数

▶ 建立 10 个元素的小堆,数据集合中前 10 个元素依次放入小堆,此时的堆顶元素是堆中最小的元素,也是堆里面第 10 个最小的元素,
▶  然后把数据集合中剩下的元素与堆顶比较,若大于堆顶则去掉堆顶,再将其插入,
▶  这样一来,堆里面存放的就是数据集合中的前 10 个最大元素,
此时小堆的堆顶元素也就是堆中的第 10 个最大的元素

 

✨思考:为什么找出最大的前10个数,不能建大堆呢?

如果你建的10个元素的大堆,堆顶元素恰好是数据集合中最大的那个,那第2大的数、第3大的数不就能找不到了。

三、完整代码和视图 

以从1w个数里找出最大的前10个数为例:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>typedef int HPDatatype;
void Swap(HPDatatype* x, HPDatatype* y)
{HPDatatype temp = 0;temp = *x;*x = *y;*y = temp;
}void AdjustDown(HPDatatype* a,int n,int parent)
{// 左孩子int child = parent * 2 + 1;// 防止越界while (child < n){//小堆if (child + 1 < n && a[child] > a[child + 1]){child++;}// 开始向下调整if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void TopK(HPDatatype* a, int n, int k)
{HPDatatype* kminHeap = (HPDatatype*)malloc(sizeof(HPDatatype) * k);assert(kminHeap);// 1. 建堆----用a中前k个元素建堆for (int i = 0; i < k; i++){kminHeap[i] = a[i];}// 建小堆for (int j = ((n - 1) - 1) / 2; j >= 0; j--){// 从倒数第一个非叶子节点开始AdjustDown(kminHeap, k, j);}// 2. 将剩余n-k个元素依次与堆顶的元素交换,比堆顶大,交换for (int i = k; i < n; i++){if (a[i] > kminHeap[0]){kminHeap[0] = a[i];//如果比堆顶大,就替换AdjustDown(kminHeap, k, 0);//向下调整确保为堆}}for (int j = 0; j < k; j++){printf("%d ", kminHeap[j]);}printf("\n");free(kminHeap);
}int main()
{int n = 10000;int* a = (int*)malloc(sizeof(int) * n);srand(time(0));for (int i = 0; i < n; ++i){a[i] = rand() % 1000000; //产生一个随机数,数值均小于100万}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;TopK(a, n, 10);return 0;
}

四、共勉

 以下就是我对数据结构---堆排序的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对数据结构-------链式二叉树请持续关注我哦!!!!

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

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

相关文章

Springboot场景开发多面手

LinkedBear &#xff0c;资深 Java 高级工程师&#xff0c;底层技术研究者与分享者&#xff0c;倾心研究 Spring 技术体系多年&#xff0c;对 Spring、Spring Boot 、SpringCloud 等框架有独到的见解&#xff0c;拥有丰富的框架体系实践经验和架构封装经验。善于总结、输出&…

CharacterEncodingFilter的用法

CharacterEncoding是SpringMVC提供的一个一个过滤器,用于设置请求和响应的字符编码,解决乱码问题,他本身是一个过滤器 那么在SpringBoot中,CharacterEncoding就有一个很好的秒用 setEncoding("UTF-8")设置编码 setForceEncoding(true) 设置请求和响应编码 还需要在配…

leetcode做题笔记160. 相交链表

给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返回结果后&…

计算机视觉——飞桨深度学习实战-深度学习网络模型

深度学习网络模型的整体架构主要数据集、模型组网以及学习优化过程三部分&#xff0c;本章主要围绕着深度学习网络模型的算法架构、常见模型展开了详细介绍&#xff0c;从经典的深度学习网络模型以CNN、RNN为代表&#xff0c;到为了解决显存不足、实时性不够等问题的轻量化网络…

Day-05 CentOS7.5 安装docker

参考 &#xff1a; Install Docker Engine on CentOS | Docker DocsLearn how to install Docker Engine on CentOS. These instructions cover the different installation methods, how to uninstall, and next steps.https://docs.docker.com/engine/install/centos/ Doc…

计算机网络 第二章物理层

计算机网络第二章知识点速刷 其中重要的是信源和信宿&#xff0c;以及调制解调器在通信模型当中起到的作用。 单工、半双工和全双工。

【Java 进阶篇】JDBC 数据库连接池详解

数据库连接池是数据库连接的管理和复用工具&#xff0c;它可以有效地降低数据库连接和断开连接的开销&#xff0c;提高了数据库访问的性能和效率。在 Java 中&#xff0c;JDBC 数据库连接池是一个常见的实现方式&#xff0c;本文将详细介绍 JDBC 数据库连接池的使用和原理。 1…

计组——I/O方式

一、程序查询方式 CPU不断轮询检查I/O控制器中“状态寄存器”&#xff0c;检测到状态为“已完成”之后&#xff0c;再从数据寄存器取出输入数据。 过程&#xff1a; 1.CPU执行初始化程序&#xff0c;并预置传送参数&#xff1b;设置计数器、设置数据首地址。 2. 向I/O接口发…

腾讯云服务器选购指南:如何选择一台合适的云服务器配置?

腾讯云服务器配置如何选择&#xff1f;CPU内存、带宽和系统盘怎么选择合适&#xff1f;个人用户可以选择轻量应用服务器&#xff0c;企业用户可以选择云服务器CVM&#xff0c;2核2G3M带宽轻量服务器95元一年、2核4G5M服务器168元一年&#xff0c;企业用户可以选择标准型S5云服务…

1300*C. Rumor(并查集贪心)

解析&#xff1a; 并查集&#xff0c;求每个集合的最小费用。 每次合并集合的时候&#xff0c;根节点保存当前集合最小的费用。 #include<bits/stdc.h> using namespace std; #define int long long const int N1e55; int n,m,a[N],p[N],cnt[N]; int find(int x){retur…

分布式锁:四种方案解决商品超卖的方案

一 分布式锁 1.1 分布式锁的作用 在多线程高并发场景下&#xff0c;为了保证资源的线程安全问题&#xff0c;jdk为我们提供了synchronized关键字和ReentrantLock可重入锁&#xff0c;但是它们只能保证一个工程内的线程安全。在分布式集群、微服务、云原生横行的当下&#xff…

深度学习笔记_4、CNN卷积神经网络+全连接神经网络解决MNIST数据

1、首先&#xff0c;导入所需的库和模块&#xff0c;包括NumPy、PyTorch、MNIST数据集、数据处理工具、模型层、优化器、损失函数、混淆矩阵、绘图工具以及数据处理工具。 import numpy as np import torch from torchvision.datasets import mnist import torchvision.transf…

uniapp 实现下拉筛选框 二次开发定制

前言 最近又收到了一个需求&#xff0c;需要在uniapp 小程序上做一个下拉筛选框&#xff0c;然后找了一下插件市场&#xff0c;确实有找到&#xff0c;但不过他不支持搜索&#xff0c;于是乎&#xff0c;我就自动动手&#xff0c;进行了二开定制&#xff0c;站在巨人的肩膀上&…

归并排序及其非递归实现

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 归并排序递归实现 归并排序非递归实现 归并排序递归实现 图示&#xff1a; 代码&#xff1a; 先分再归并&#xff0c;像是后序一般。 //归并排序 void MergeSort(int* arr, int left, int right) {int* temp (int…

tcp滑动窗口原理

18.1 滑动窗口 我们再来看这个比喻&#xff1a; 网络仅仅是保证了整个网络的连通性&#xff0c;我们我们基于整个网络去传输&#xff0c;那么是不是我想发送多少数据就发送多少数据呢&#xff1f;如果是这样的话&#xff0c;是不是就会像我们的从一个池塘抽水去灌到另外一个…

速看:免费领取4台阿里云服务器_申请入口及领取流程

注册阿里云账号&#xff0c;免费领云服务器&#xff0c;最高领取4台云服务器&#xff0c;每月750小时&#xff0c;3个月免费试用时长&#xff0c;可快速搭建网站/小程序&#xff0c;部署开发环境&#xff0c;开发多种企业应用。阿里云服务器网分享阿里云服务器免费领取入口、免…

openGauss学习笔记-89 openGauss 数据库管理-内存优化表MOT管理-内存表特性-使用MOT-MOT使用查询原生编译

文章目录 openGauss学习笔记-89 openGauss 数据库管理-内存优化表MOT管理-内存表特性-使用MOT-MOT使用查询原生编译89.1 查询编译&#xff1a;PREPARE语句89.2 运行命令89.3 轻量执行支持的查询89.4 轻量执行不支持的查询89.5 JIT存储过程89.6 MOT JIT诊断89.6.1 mot_jit_detai…

Pygame中监控鼠标动作的方法

在Pygame中监控键盘按键的方法_pygame获取键盘输入-CSDN博客中提到&#xff0c;通过在while True循环中获取队列中事件的方法监控键盘动作。监控鼠标动作的方法与监控键盘动作的方法相同。 相关连接1 队列与事件的相关知识&#xff0c;请参考 Pygame中监控键盘按键的方法_pyg…

网络爬虫--伪装浏览器

从用户请求的Headers反反爬 在访问某些网站的时候&#xff0c;网站通常会用判断访问是否带有头文件来鉴别该访问是否为爬虫&#xff0c;用来作为反爬取的一种策略。很多网站都会对Headers的User-Agent进行检测&#xff0c;还有一部分网站会对Referer进行检测&#xff08;一些资…

电脑通过串口助手和51单片机串口通讯

今天有时间把电脑和51单片机之间的串口通讯搞定了&#xff0c;电脑发送的串口数据&#xff0c;单片机能够正常接收并显示到oled屏幕上&#xff0c;特此记录一下&#xff0c;防止后面自己忘记了怎么搞得了。 先来两个图片看看结果吧&#xff01; 下面是串口3.c的文件全部内容&a…