top-k类问题

问题描述

从arr[1, n]这n个数中,找出最大的k个数,这就是经典的TopK问题。

1 直接排序

  排序是最容易想到的方法,将n个数排序之后,取出最大的k个,即为所得:

sort(arr, 1, n);  // 时间复杂度为O(n*lg(n))
return arr[1, k];  //全局的排序浪费大量时间,题中只需最大n个即可

2 局部排序

  冒泡是一个很常见的排序方法,每冒一个泡,找出最大值,冒k个泡,就得到top-k:

sort(arr, 1, n);  // 时间复杂度为O(n*lg(n))
return arr[1, k];  //全局的排序浪费大量时间,题中只需最大n个即可

3 堆

  思路:只找到TopK,不排序TopK。先用前k个元素生成一个小顶堆,这个小顶堆用于存储,当前最大的k个元素。接着,从第k+1个元素开始扫描,和堆顶(堆中最小的元素)比较,如果被扫描的元素大于堆顶,则替换堆顶的元素,并调整堆,以保证堆内的k个元素,总是当前最大的k个元素。直到,扫描完所有n-k个元素,最终堆中的k个元素,就是猥琐求的TopK。

heap[k] = make_heap(arr[1, k]);
for(i=k+1 to n)
{adjust_heap(heap[k], arr[i]); //时间复杂度:O(n*lg(k))
}
return heap[k];

  堆,将冒泡的TopK排序优化为了TopK不排序,节省了计算资源,是求TopK的经典算法。

4 随机选择

  随机选择算在是《算法导论》中一个经典的算法,其时间复杂度为O(n),是一个线性复杂度的方法。不妨先了解前序知识:快速排序。

4.1 快速排序

  快速排序的核心为分治法。

4.1.1 分治法

  分治法(Divide & Conquer),指把一个大的问题,转化为若干个子问题(Divide),每个子问题“都”解决,大的问题便随之解决(Conquer)。这里的关键词是“都”。从伪代码里可以看到,快速排序递归时,先通过partition把数组分隔为两个部分,两个部分“都”要再次递归。

void quick_sort(int[]arr, int low, int high)
{if (low >= high) return; int i = partition(arr, low, high); // the keyquick_sort(arr, low, i - 1); quick_sort(arr, i + 1, high);
}
4.1.2 减治法

  分治法有一个特例,叫减治法(Reduce & Conquer)。把一个大的问题,转化为若干个子问题(Reduce),这些子问题中“只”解决一个,大的问题便随之解决(Conquer)。这里的关键词是“只”。

  二分查找binary_search,BS,是一个典型的运用减治法思想的算法,其伪代码是:

int BS(int[]arr, int low, int high, int target) 
{if (low > high) return -1; int mid = low + ((high - low) >> 1);if (arr[mid] == target) return mid; else if (arr[mid] > target) return BS(arr, low, mid - 1, target);else return BS(arr, mid + 1, high, target); 
}

  从伪代码可以看到,二分查找,一个大的问题,可以用一个mid元素,分成左半区,右半区两个子问题。而左右两个子问题,只需要解决其中一个,递归一次,就能够解决二分查找全局的问题。

  可以发现快速排序时间复杂度为:O(n*lg(n)),而二分查找为:O(lg(n))。

4.1.3 partition

  顾名思义,partition会把整体分为两个部分。更具体的,会用数组arr中的一个元素(默认是第一个元素t=arr[low])为划分依据,将数据arr[low, high]划分成左右两个子数组:左边比t大,右边比t小,中间位置i为划分元素。

  把整个数组扫一遍后partition的时间复杂度为O(n)。


  在解决top-k类问题时,可以用大partition的思想:top-k是希望求出arr[1,n]中最大的k个数,那如果找到了第k大的数,做一次partition,不就一次性找到最大的k个数了么?问题变成了arr[1, n]中找到第k大的数。

  再回过头来看看第一次partition,划分之后,i = partition(arr, 1, n):

  • 如果i大于k,则说明arr[i]左边的元素都大于k,于是只递归arr[1, i-1]里第k大的元素即可。

  • 如果i小于k,则说明说明第k大的元素在arr[i]的右边,于是只递归arr[i+1, n]里第k-i大的元素即可。

  这就是随机选择算法(randomized_select,RS),其伪代码如下:

int RS(int[]arr, int low, int high, int k) 
{if (low == high) return arr[low];  int i = partition(arr, low, high); int t = i - low;  // 计算左半部分元素个数if (t >= k)return RS(arr, low, i - 1, k);  // 左半部分查找第k小元素elsereturn RS(arr, i + 1, high, k - t);  // 右半部分查找第(k-t)小元素
}

  这是一个典型的减治算法,递归内的两个分支,最终只会执行一个,它的时间复杂度是O(n)。

整体的代码如下:

#include<iostream>
using namespace std;
int q[10000] ,n ,k ;int quickSort(int *a ,int l ,int r ,int k)
{if(l >= r) return 0 ;int i = l - 1 ,j = r + 1 ,m = a[l + j >> 1] ;while(i < j){do j -- ;while(a[j] > m);do i ++ ;while(a[i] < m);if(i < j) swap(a[i],a[j]);}int t = i - l ;if(t >= k) return quickSort(a ,l ,j ,k );else return quickSort(a ,j + 1 ,r ,k );
}int main()
{cin >> n >> k;for(int i = 0 ;i < n ;i ++) cin >> q[i];quickSort(q ,0 ,n - 1 ,k );for(int i = n - k ;i < n ;i ++) cout << q[i] << " " ;return 0;
}
//input:
10 2
9 3 4 8 7 1 10 5 2 6
//output:
10 9

  确定其中第k个最大值的解法:

  1. 选择排序或交互排序,K次选择后即可得到第k大的数。总的时间复杂度为O(nk).
  2. 用O(4n)的方法对原数组建最大堆,然后pop出k次即可。时间复杂度为O(4n + k*logn).
  3. 利用hash保存数组中元素q[i]出现的次数,利用计数排序的思想,线性从大到小扫描过程中,前面有k-1个数则为第k大数,平均情况下时间复杂度O(n).
  4. STL中可以用nth_element求得类似的第n大的数(由谓词决定),还可以用partial_sort对区间进行部分排序,得到类似前k大的数(由谓词决定),

总结

top-k类问题的思路变化:

  • 全局排序,O(n*lg(n));
  • 局部排序,只排序TopK个数,O(n*k);
  • 堆,TopK个数也不排序了,O(n*lg(k));
  • 分治法,每个分支“都要”递归,例如:快速排序,O(n*lg(n));
  • 减治法,“只要”递归一个分支,例如:二分查找O(lg(n)),随机选择O(n);
  • TopK的另一个解法:随机选择+partition。

本文为学习随笔,附上原文链接 一次搞透,面试中的TopK问题!,寻找第K大的数的方法总结

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

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

相关文章

设计模式之适配器模式(从多个MQ消息体中,抽取指定字段值场景)

前言 工作到3年左右很大一部分程序员都想提升自己的技术栈&#xff0c;开始尝试去阅读一些源码&#xff0c;例如Spring、Mybaits、Dubbo等&#xff0c;但读着读着发现越来越难懂&#xff0c;一会从这过来一会跑到那去。甚至怀疑自己技术太差&#xff0c;慢慢也就不愿意再触碰这…

万字长文解读深度学习——循环神经网络RNN、LSTM、GRU、Bi-RNN

推荐阅读&#xff1a; 深度学习知识点全面总结 如何从RNN起步&#xff0c;一步一步通俗理解LSTM 深度学习之RNN(循环神经网络) 循环神经网络&#xff08;RNN与LSTM&#xff09; 文章目录 &#x1f33a;深度学习面试八股汇总&#x1f33a;文本特征提取的方法1. 基础方法1.1 词袋…

Qt 使用QTreeView显示并动态的增删改查JSON文件数据

文章目录 效果图概述部分代码总结 效果图 概述 本案例在此开源项目QJsonModel的基础上实现&#xff0c;动态的生成并操作JSON数据&#xff0c;QJsonModel是一个基于QAbstractItemModel的JSON数据模型&#xff0c;它提供了一种简单的方式来将JSON数据可视化&#xff0c;功能简单…

基于Springboot+Vue的游乐园管理系统 (含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 该系统…

漫谈MCU优化:从硬件设计优化到可靠性挑战

1.关于MCU 微控制器&#xff08;Microcontroller Unit, MCU&#xff09;&#xff0c;是以微处理器为基础&#xff0c;加上存储器以及计数器、I2C、UART等外设模块与接口电路整合的单芯片微型计算机。 ▲MCU实物图 MCU拥有性能好、可编程、灵活度高、功耗低等优点&#xff0c;…

【深度学习】— 多输入多输出通道、多通道输入的卷积、多输出通道、1×1 卷积层、汇聚层、多通道汇聚层

【深度学习】— 多输入多输出通道、多通道输入的卷积、多输出通道、11 卷积层、汇聚层、多通道汇聚层 多输入多输出通道多通道输入的卷积示例&#xff1a;多通道的二维互相关运算 多输出通道实现多通道输出的互相关运算 11 卷积层11 卷积的作用 使用全连接层实现 11 卷积小结 …

如何在c++侧编译运行一个aclnn(AOL)算子?

1 AOL算子库 CANN&#xff08;Compute Architecture for Neural Networks&#xff09;提供了算子加速库&#xff08;Ascend Operator Library&#xff0c;简称AOL&#xff09;。该库提供了一系列丰富且深度优化过的高性能算子API&#xff0c;更亲和昇腾AI处理器&#xff0c;调…

IDEA git提交时如何忽略某个文件或文件夹

步骤如下 英文界面操作顺序 打开file——>settings——>Editor——>File Types 中文插件操作顺序 打开 文件——>设置——>编辑器——> 文件类型 安装下面的操作顺序添加想要屏蔽文件类型后缀即可&#xff1a;

《常用深度学习神经网络及其原理与应用场景》

一、总体介绍 一、引言 随着科技的不断发展&#xff0c;深度学习已经成为人工智能领域中最具影响力的技术之一。深度学习神经网络通过模拟人类大脑的神经元结构和工作方式&#xff0c;能够自动学习数据中的特征和模式&#xff0c;从而实现各种复杂的任务&#xff0c;如图像识…

科技革命前沿:救援机器人!

救援机器人主要制作材料 传统刚性材料&#xff1a;传统救援机器人多采用金属等刚性材料制作&#xff0c;以确保其结构强度和稳定性。这些材料在承受较大负载和复杂环境时表现出色&#xff0c;但可能缺乏一定的灵活性。 软体材料&#xff1a;近年来&#xff0c;软体机器人技术…

Ubuntu中以root身份运行Qt创建的项目

Ubuntu中以root身份运行Qt创建的项目 Chapter1 Ubuntu中以root身份运行Qt创建的项目解决方法&#xff1a; Chapter1 Ubuntu中以root身份运行Qt创建的项目 原文链接&#xff1a;https://blog.csdn.net/lhbaba/article/details/124733323 使用Qt开发项目时遇到了一个问题&#…

leetcode25:k个一组链表反转

给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节点内部的值…

ctfshow-web入门-反序列化(web265-web270)

目录 1、web265 2、web266 3、web267 4、web268 5、web269 6、web270 1、web265 很简单的一个判断&#xff0c;满足 $this->token$this->password; 即可 由于 $ctfshow->tokenmd5(mt_rand()) 会将 token 随机为一个 md5 值&#xff0c;我们使用 & 绕一下&am…

qt QLocale详解

1、概述 QLocale是Qt框架中的一个类&#xff0c;用于处理与本地化相关的操作。它能够方便地实现日期、时间、数字和货币的格式化和解析&#xff0c;支持不同的语言、区域设置和字符集。QLocale提供了一种跨平台的方式来获取当前系统的语言设置&#xff0c;并返回该语言的本地化…

年龄大了,听力一定会下降吗?

随着年龄的增长&#xff0c;听力下降&#xff08;也称为老年性听力损失或感音神经性聋&#xff09;确实是一个常见的现象&#xff0c;但并不是每个人都会经历明显的听力下降。以下是一些影响因素和相关信息&#xff1a; 1. 自然老化过程 •随着年龄的增长&#xff0c;内耳的毛…

Linux SSH私钥认证结合cpolar内网穿透安全高效远程登录指南

文章目录 前言1. Linux 生成SSH秘钥对2. 修改SSH服务配置文件3. 客户端秘钥文件设置4. 本地SSH私钥连接测试5. Linux安装Cpolar工具6. 配置SSHTCP公网地址7. 远程SSH私钥连接测试8. 固定SSH公网地址9. 固定SSH地址测试 前言 开发人员在工作中经常需要远程访问服务器和数据中心…

国产化浪潮下,高科技企业如何选择合适的国产ftp软件方案?

高科技企业在数字化转型和创新发展中&#xff0c;数据资产扮演着越来越重要的角色。在研发过程中产生的实验数据、设计文档、测试结果等&#xff0c;专利、商标、版权之类的创新成果等&#xff0c;随着信息量急剧增加和安全威胁的复杂化&#xff0c;传统的FTP软件已经不能满足这…

高校宿舍信息管理系统小程序

作者主页&#xff1a;编程千纸鹤 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验&#xff0c;被多个学校常年聘为校外企业导师&#xff0c;指导学生毕业设计并参…

DNS域名详细解析详解

文章目录 DNS域名详细解析详解一、引言二、DNS域名解析过程1、DNS解析概述1.1、DNS解析的基本步骤 2、代码示例 三、DNS查询类型1、递归查询2、迭代查询 四、总结 DNS域名详细解析详解 一、引言 在互联网的世界里&#xff0c;域名和IP地址是两个不可或缺的概念。IP地址是计算…

Selenium自动化测试 —— 模拟鼠标键盘的操作事件

软件测试资料领取&#xff1a;[内部资源] 想拿年薪40W的软件测试人员&#xff0c;这份资料必须领取~ 软件测试面试刷题工具&#xff1a;软件测试面试刷题【800道面试题答案免费刷】 鼠标操作事件 在实际的web产品测试中&#xff0c;对于鼠标的操作&#xff0c;不单单只有clic…