Top K问题的两种解决思路

2023-11-15

Top K问题在数据分析中非常普遍的一个问题(在面试中也经常被问到),比如:

从20亿个数字的文本中,找出最大的前100个。

解决Top K问题有两种思路,

  • 最直观:小顶堆(大顶堆 -> 最小100个数);
  • 较高效:Quick Select算法。

LeetCode上有一个问题215. Kth Largest Element in an Array,类似于Top K问题。

1. 堆

小顶堆(min-heap)有个重要的性质——每个结点的值均不大于其左右孩子结点的值,则堆顶元素即为整个堆的最小值。JDK中PriorityQueue实现了数据结构堆,通过指定comparator字段来表示小顶堆或大顶堆,默认为null,表示自然序(natural ordering)。

小顶堆解决Top K问题的思路:小顶堆维护当前扫描到的最大100个数,其后每一次的扫描到的元素,若大于堆顶,则入堆,然后删除堆顶;依此往复,直至扫描完所有元素。Java实现第K大整数代码如下:

public int findKthLargest(int[] nums, int k) {
  PriorityQueue<Integer> minQueue = new PriorityQueue<>(k);
  for (int num : nums) {
    if (minQueue.size() < k || num > minQueue.peek())
      minQueue.offer(num);
    if (minQueue.size() > k)
      minQueue.poll();
  }
  return minQueue.peek();
}

2. Quick Select

Quick Select [1]脱胎于快排(Quick Sort),两个算法的作者都是Hoare,并且思想也非常接近:选取一个基准元素pivot,将数组切分(partition)为两个子数组,比pivot大的扔左子数组,比pivot小的扔右子数组,然后递推地切分子数组。Quick Select不同于Quick Sort的是其没有对每个子数组做切分,而是对目标子数组做切分。其次,Quick Select与Quick Sort一样,是一个不稳定的算法;pivot选取直接影响了算法的好坏,worst case下的时间复杂度达到了O(n2)O(n2)。下面给出Quick Sort的Java实现:

public void quickSort(int arr[], int left, int right) {
  if (left >= right) return;
  int index = partition(arr, left, right);
  quickSort(arr, left, index - 1);
  quickSort(arr, index + 1, right);
}

// partition subarray a[left..right] so that a[left..j-1] >= a[j] >= a[j+1..right]
// and return index j
private int partition(int arr[], int left, int right) {
  int i = left, j = right + 1, pivot = arr[left];
  while (true) {
    while (i < right && arr[++i] > pivot)
      if (i == right) break;
    while (j > left && arr[--j] < pivot)
      if (j == left) break;
    if (i >= j) break;
    swap(arr, i, j);
  }
  swap(arr, left, j);  // swap pivot and a[j]
  return j;
}

private void swap(int[] arr, int i, int j) {
  int tmp = arr[i];
  arr[i] = arr[j];
  arr[j] = tmp;
}

Quick Select的目标是找出第k大元素,所以

  • 若切分后的左子数组的长度 > k,则第k大元素必出现在左子数组中;
  • 若切分后的左子数组的长度 = k-1,则第k大元素为pivot;
  • 若上述两个条件均不满足,则第k大元素必出现在右子数组中。

Quick Select的Java实现如下:

public int findKthLargest(int[] nums, int k) {
  return quickSelect(nums, k, 0, nums.length - 1);
}

// quick select to find the kth-largest element
public int quickSelect(int[] arr, int k, int left, int right) {
  if (left == right) return arr[right];
  int index = partition(arr, left, right);
  if (index - left + 1 > k)
    return quickSelect(arr, k, left, index - 1);
  else if (index - left + 1 == k)
    return arr[index];
  else
    return quickSelect(arr, k - index + left - 1, index + 1, right);

}

上面给出的代码都是求解第k大元素;若想要得到Top K元素,仅需要将代码做稍微的修改:比如,扫描完成后的小顶堆对应于Top K,Quick Select算法用中间变量保存Top K元素。

3. 参考资料

[1] Hoare, Charles Anthony Richard. "Algorithm 65: find." Communications of the ACM 4.7 (1961): 321-322.
[2] James Aspnes, QuickSelect.

如需转载,请注明作者及出处.
作者: Treant

出处:http://www.cnblogs.com/en-heng/




https://www.cnblogs.com/en-heng/p/6336625.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Top K问题的两种解决思路 的相关文章

  • 在哪里可以找到有关双三次插值和 Lanczos 重采样的好读物?

    我想用 C 实现上述两种图像重采样算法 双三次和 Lanczos 我知道现有的实现有几十种 但我仍然想制作自己的实现 我之所以这么做 部分原因是我想了解它们是如何工作的 部分原因是我想为它们提供一些主流实现中没有的功能 例如可配置的多 CP
  • 比较两棵树的伪代码

    这是我遇到过几次的问题 并且不确信我使用了最有效的逻辑 例如 假设我有两棵树 一棵是文件夹结构 另一棵是该文件夹结构的内存 模型 我希望比较两棵树 并生成一棵树中存在的节点列表 而不是另一棵树中存在的节点列表 反之亦然 是否有公认的算法来处
  • 对列表中的相邻元素进行分组

    假设我想编写一个函数来执行此操作 输入 1 1 3 3 4 2 2 5 6 6 输出 1 1 3 3 4 2 2 5 6 6 它将相同的相邻元素分组 这个方法的名称应该是什么 此操作有标准名称吗 In 1 1 3 3 4 2 2 5 6 6
  • 加权图的 BFS 算法 - 寻找最短距离

    我看过很多帖子 即 post1 https stackoverflow com questions 30409493 using bfs for weighted graphs post2 https cs stackexchange co
  • 查找数组中的重叠数据

    我们正在编写一个 C 应用程序 它将有助于删除不必要的数据重复器 只有在以下情况下才可以移除中继器 all它接收到的数据被其他中继器接收 我们第一步需要做的事情解释如下 例如 我有 int 数组的集合 A 1 2 3 4 5 b 2 4 6
  • 仅使用两个变量交换两个数字

    它如何执行交换 a a b b a b a b a 我不同意把它换成书 书中的选项包括 a和b的值的补集 否定和b 希望这些选项也不能满足它 正确的算法应该是 a a b b a b a a b
  • RNG 技术的可移植性和可重复性

    我可以使用两种方法之一来创建一个伪随机数序列 该序列具有两个重要特征 1 它可以在不同的机器上重现 2 该序列永远不会重复范围内的数字 直到所有数字都被发出 我的问题是 这两种方法在可移植性 操作系统 Python 版本等 方面是否存在潜在
  • Python 将字符串组合成尽可能短的字符串?

    如果我有一个字符串列表 我想将它们组合成一个具有重叠字符的字符串 如果没有剩余的重叠字符串 请将其添加到末尾 这是一个过于简化的版本 input one two output twone 我正在寻找一种方法来对输入列表中的任意数量的字符串执
  • 正则表达式等价

    有没有办法找出两个任意正则表达式是否等价 对我来说看起来很复杂的问题 但可能有一些 DFA 简化机制之类的 要测试等价性 您可以计算的表达式并进行比较
  • 为什么对本地列表求和比用“GHC -O2”对教会编码列表求和慢?

    为了测试教会编码的列表如何针对用户定义的列表和本机列表执行 我准备了 3 个基准测试 用户定义的列表 data List a Cons a List a Nil deriving Show lenumTil n go n Nil where
  • 使用向量的 merge_sort 在少于 9 个输入的情况下效果很好

    不知何故 我使用向量实现了合并排序 问题是 它可以在少于 9 个输入的情况下正常工作 但在有 9 个或更多输入的情况下 它会执行一些我不明白的操作 如下所示 Input 5 4 3 2 1 6 5 4 3 2 1 9 8 7 6 5 4 3
  • Java中获取集合的幂集

    的幂集为 1 2 3 is 2 3 2 3 1 2 1 3 1 2 3 1 假设我有一个Set在爪哇中 Set
  • 期望最大化算法的数值示例[重复]

    这个问题在这里已经有答案了 由于我不确定给出的公式 有人可以提供 EM 算法的简单数字示例吗 一个非常简单的具有 4 或 5 个笛卡尔坐标的坐标就可以了 那这个呢 http en wikibooks org wiki Data Mining
  • 以编程方式分解大量数字

    好吧 所以我有一个巨大的数字f 实际上 这个数字只有 100 多位数字长 我知道这些因子的大小大致相同 如果我的资源和时间有限 我应该使用什么语言和算法 我包括在限制时间内编写算法的时间长度 想法 编辑 我所说的有限是指在尽可能短的时间内
  • 将两个大数作为字符串相除,而不使用java中的Bignumbers

    我需要在不使用 Biginteger 的情况下划分两个大整数 因为数字不能存储在原始类型中 因为我需要从给定的字符串中逐个字符地执行此操作 我已经创建了一个名为 BigNumber 的类 用这个类我可以 Add multiply 比较两个内
  • 给定一个零索引数组 & 该数组的平衡索引[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 给出一个由 N 个整数组成的零索引数组 A 该数组的平衡索引是任何整数 P 满足 0 P 例如 考虑以下由 N 8 个元素组成的数组
  • 有人真正有效地实现了斐波那契堆吗?

    你们中有人曾经实施过斐波那契堆 http en wikipedia org wiki Fibonacci heap 几年前我就这样做了 但它比使用基于数组的 BinHeaps 慢了几个数量级 当时 我认为这是一个宝贵的教训 告诉我们研究并不
  • 欧拉项目 #18 方法

    我正在研究一个欧拉项目 具体来说 18 总而言之 这个想法是从三角形中找到最大路径 3 7 4 2 4 6 8 5 9 3 3 7 4 9 23 读到这里 大多数人表示 通过从下到上工作可以正确解决这个问题 而不是使用从上到下 贪婪 工作的
  • 归并排序究竟进行了多少次比较?

    我读到 在实践中 快速排序比合并排序快得多 其原因是隐藏常量 那么 随机快速排序复杂度的解是2nlnn 1 39nlogn 这意味着快速排序中的常数是1 39 但是合并排序呢 归并排序中的常数是什么 让我们看看能否解决这个问题 在合并排序中
  • 证明字符串算法[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi

随机推荐

  • Log4j2之JNDI注入(CVE-2021-44228)

    前言 首先要了解什么是Log4j2 Log4j2是一个Java日志组件 主要用于对日志的记录 这次漏洞出现在Log4j2的Lookup功能 使用Lookup可以在日志中添加动态的值 这些变量可以是外部环境变量 也可以是MDC中的变量 还可以
  • 海量数据库(详解缓存处理方法)

    缓存处理大数据 缓存就是将从数据库中获取的结果暂时保存起来在下次使用的时候无需重新到数据库中获取 从而降低数据库的压力 缓存的使用方式可以分为通过程序直接将数据库数据保存到内存中和使用缓存框架两种方式 它主要用于数据变化不是很频繁的情况 而
  • OR36 链表的回文结构

    OR36 链表的回文结构 较难 通过率 29 47 时间限制 3秒 空间限制 32M 知识点 链表栈 描述 对于一个链表 请设计一个时间复杂度为O n 额外空间复杂度为O 1 的算法 判断其是否为回文结构 给定一个链表的头指针A 请返回一个
  • python中抽象类和抽象方法_在Python中定义和使用 抽象类及抽象方法 抽象属性

    原文链接 http www jb51 net article 87710 htm 本文根据自己的理解和思考 对原文略有改动 Python中我们可以使用abc模块来构建抽象类 在讲抽象类之前 先说下抽象方法的实现 抽象方法是基类中定义的方法
  • 【MMDet Note】MMDetection中Neck之FPN代码理解与解读

    文章目录 前言 一 总概 二 代码解读 1 FPN类 2 def forward 总结 前言 mmdetection mmdet models necks fpn py中FPN类的个人理解与解读 一 总概 本文以mmdetection co
  • vscode快捷键(全局搜索等

    vscode其实有强大的快捷键搜索功能 全部快捷键可以参考官网 Visual Studio Code Key Bindings Mac快捷键 https code visualstudio com shortcuts keyboard sh
  • 【IDEA】idea Gradle 里面java类显示为灰色

    文章目录 1 概述 2 第一步 1 概述 IDEA下导入了es源码 并且编译成功 参考 Elasticsearch es 6 8 编译成功 但是看源码的时候 却发现部分为黑色 2 第一步 找到父项目 点击右键 选择Open Module S
  • 睿智的目标检测36——Pytorch搭建Efficientdet目标检测平台

    睿智的目标检测33 Pytorch搭建Efficientdet目标检测平台 学习前言 什么是Efficientdet目标检测算法 源码下载 Efficientdet实现思路 一 预测部分 1 主干网络介绍 2 BiFPN加强特征提取 3 从
  • C++获取Unix时间戳(分别以秒和毫秒为单位)的几种方法

    文章目录 前言 正文 1 调用ctime库 2 调用chrono 3 调用sys timeb h 总结 前言 有时需要打印当前的绝对时间 并计算时间间隔 Unix时间戳是一种很好的时间记录标准 表示从1970年1月1日 UTC GMT的午夜
  • Ubuntu系统安装中文输入法教程

    新安装的Ubuntu系统由于无法进行中文输入 经过排查找到解决方法 Ubuntu系统安装中文输入法教程 在Vmware虚拟机中安装好Ubuntu系统 但是一般情况下无法使用中文输入 需要使用中午输入的时候非常不方便 可以通过在终端中输入以下
  • ESP32基础应用之LVGL基础

    文章目录 1 实验目的 1 1 参考文章 2 实验工具 3 准备工作 3 1 搭建ESP32开发环境 3 2 克隆lv port esp32工程 4 配置lv port esp32工程 5 实验验证 6 使用过程遇到的问题 6 1 触摸功能
  • Google guava之BiMap简介说明

    转自 Google guava之BiMap简介说明 下文笔者讲述guava中BiMap集合的简介说明 如下所示 guava之BiMap集合简介 BiMap集合 用于实现key和value翻转 BiMap可进行正排索引和倒排索引 注意事项 b
  • SpringMVC中整合XML、JSON试图一

    http www cnblogs com hoojo archive 2011 04 29 2032571 html SpringMVC中整合了JSON XML的视图 可以通过这些视图完成Java对象 到XML JSON的转换 转换XML提
  • 1.centOS7 下载安装教程

    1 下载镜像 我选择的是阿里的镜像没去官网 官网下载太慢 阿里云开源镜像站 阿里巴巴开源镜像站 OPSX镜像站 阿里云开发者社区 点击centos下面选择版本 根据自己需要选择 我选择的是isos 后面的名称 我下面为大家一一介绍下 DVD
  • Arduino Uno开发板+电机驱动扩展版CNC Shield V3.0硬件说明

    陈拓 2023 03 24 2023 03 29 1 Arduino CNC Shield V3 00电机驱动扩展版 注意 板子左侧中间的玻璃管是玻封保险丝 Arduino CNC Shield可以搭载A4988 DRV8825等步进电机驱
  • js逆向(MD5)

    实现中关村MD5登录 比较简单 网址 https www zol com cn 点击登录按钮输入自己的账号和密码 先注册自己的账号和密码 防止ajax请求不到 找到加密的参数 输入账号和密码 调出开发者工具 在fetch xhr这一栏中也是
  • conda和docker的环境打包

    conda 需安装conda pack 命令 conda pack n env o env tar gz ignore editable packages 如果安装了可编辑的包 加上 ignore editable packages doc
  • google api设计指南-简介

    简介 这是联网 API 的通用设计指南 它自 2014 年起在 Google 内部使用 是 Google 在设计 Cloud API 和其他 Google API 时遵循的指南 此设计指南在此处共享 以便为外部开发者提供信息 并使我们所有人
  • 一句Python命令启动一个Web服务器

    利用Python自带的包可以建立简单的web服务器 在DOS里cd到准备做服务器根目录的路径下 输入命令 python m Web服务器模块 端口号 默认8000 例如 python m SimpleHTTPServer 8080 然后就可
  • Top K问题的两种解决思路

    Top K问题在数据分析中非常普遍的一个问题 在面试中也经常被问到 比如 从20亿个数字的文本中 找出最大的前100个 解决Top K问题有两种思路 最直观 小顶堆 大顶堆 gt 最小100个数 较高效 Quick Select算法 Lee