总结:无序的n个数中找第k大;n海量时咋办;k海量时又咋办(桶排序)

目录

  • 如何在无序的n个数中找到第k大的数?
  • 如何在海量无序的n个数中找到第k大的数?
  • 如果k大到连k个数都不能放入内存呢?

最近面试遇到了这个问题,主要考察求职者一定的算法能力及思路,便想将这个问题及其变种问题系统的总结一下,将我个人的解决思路给大家分享一下。

如何在无序的n个数中找到第k大的数?

首先回顾一下这个问题的最初版本,即非海量数据的情况下。

基础做法:

  • 使用最小堆: 维护一个大小为 k 的最小堆,遍历数组,将元素加入堆中,如果堆的大小超过 k,则移除堆顶元素。最终堆顶元素即为第 k 大的元素。时间复杂度为 O(nlogk)
  • 完全排序: 将数组排序后直接取第 k 个元素,但时间复杂度较高,为 O(nlogn)

要在无序的 n 个数中高效地找出第 k 大的元素,可以使用 快速选择算法(Quickselect)。这是一个平均时间复杂度为 O(n) 的算法,而且是原地算法,不需要额外的存储空间 ,专门用于在无序数组中寻找第 k 大或第 k 小的元素。

快速选择算法的基本思想:

  • 1、选择一个枢纽元(Pivot): 通常随机选择数组中的一个元素作为枢纽元。

  • 2、划分数组: 通过一次遍历,将数组分成两部分:

    • 左侧的元素都大于等于枢纽元(如果寻找第 k 大的元素)。
    • 右侧的元素都小于枢纽元。
  • 3、递归选择

    • 如果左侧部分的元素数量等于 k,那么左侧部分的最后一个元素即为第 k 大的元素。
    • 如果左侧部分的元素数量大于 k,在左侧部分递归执行上述步骤。
    • 如果左侧部分的元素数量小于 k,在右侧部分寻找第 k−左侧元素数量 大的元素。


如何在海量无序的n个数中找到第k大的数?

要在无法一次性将所有数据读入内存的情况下找到第 k 大的元素,可以采用分块处理的方法:

使用大小为 k 的最小堆(Min-Heap):

  • 1、初始化最小堆: 创建一个容量为 k 的最小堆,用于存储当前找到的前 k 大的元素。

  • 2、分批读取数据: 由于内存限制,我们不能一次性读取所有数据。因此,将数据分成可以容纳于内存的小批次进行处理。

  • 3、处理每个元素。对于每个读取的元素 x:
    如果最小堆的大小小于 k,则直接将 x 插入堆中。
    如果堆已满且 x 大于堆顶元素(即当前堆中最小的元素),则将堆顶元素替换为 x,并对堆进行调整以维持最小堆的性质。
    如果 x 小于或等于堆顶元素,忽略它,因为它不可能是前 k 大的元素。

  • 4、迭代处理: 重复步骤2和3,直到所有数据都被处理完。

  • 5、获取结果: 在所有数据处理完毕后,最小堆中的元素就是数据集中的前 k 大元素,堆顶元素即第 k 大的元素。

优点:内存效率高: 由于堆的大小固定为 k,只需在内存中维护 k 个元素,适用于超大规模的数据集。
时间效率高: 每次插入或替换堆顶元素的操作时间复杂度为 O(logk),总体时间复杂度为 O(nlogk)

优化方案多线程并行处理
为每个数据块启动一个线程(或进程),在各自的线程中执行以下操作:
局部求解: 使用大小为 k 的最小堆,在自己的数据块中找到该块的前 k 大元素。
合并局部结果:在所有线程完成后,将每个线程得到的前 k 大元素进行合并,得到全局的前 k 大元素。具体的合并方法如下:
汇总所有最小堆: 将所有线程返回的最小堆中的元素汇总成一个列表,总大小为 k×s(其中 s 为线程数)。
再次构建最小堆: 在合并后的列表上再次构建一个大小为 k 的最小堆,找出全局的前 k 大元素。



如果k大到连k个数都不能放入内存呢?

如果n和k都很大,即k个数都不能被一次性读入到内存,即假设有一万个数,k是400,但是内存只能存放100个数(假设),这种情况下,怎么样高效找出第k大的元素呢?

可以采用 多次遍历的数据分桶或称桶排序 的方法。以下是具体步骤:

  • 1、第一次遍历(计算最小值和最大值):由于内存只能容纳 100 个数,我们无法一次性读取所有数据。我们可以顺序读取数据,在此过程中,更新并记录整个数据集的最小值(min)和最大值(max)。
  • 2、建立桶的范围:根据第一次遍历得到的 min 和 max,将整个数据范围划分为 B 个桶(这里 B≤100 以适应内存限制)。每个桶代表一个数值区间,区间大小为 (max−min)/B。
  • 3、第二次遍历(统计每个桶中的元素数量):再次顺序读取数据,根据每个数的值,确定其所属的桶,并增加该桶的计数器。在遍历过程中,仅需在内存中维护一个大小为 B 的计数数组。
  • 4、确定包含第 k 大元素的桶:从最大值所在的桶开始,累加每个桶的元素数量。当累加的元素数量达到或超过 k 时,记录当前桶为目标桶。这意味着第 k 大的元素必定在这个桶的范围内。
  • 5、缩小范围并递归处理:如果目标桶中的元素数量仍然超过内存容量,则对该桶再次进行步骤 2 到步骤 4 的处理。
    在新的范围内,将目标桶进一步划分为更细的桶,重复上述过程。这个过程可能需要多次迭代,每次都将范围缩小,直到目标桶内的元素数量可以被内存容纳。
  • 6、在内存中找出第 k 大元素:当目标桶内的元素数量小于或等于内存容量时,将这些元素全部读入内存。对这些元素进行排序或使用适当的选择算法,根据已经遍历过的元素数量,便可在直接内存中找出第 k 大元素。

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

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

相关文章

C++【类和对象】(一)

文章目录 前言1.类的定义1.1类定义格式1.2 访问限定符1.3 类域 2. 实例化2.1 实例化的概念2.2 对象大小 3.this指针结语 前言 在前文我们讲解了C基础语法知识。本文将会讲解C的类和对象。 1.类的定义 1.1类定义格式 class name {};class为定义类的关键字&#x…

Linux进阶命令-rsync

作者介绍:简历上没有一个精通的运维工程师。希望大家多多关注作者,下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 经过上一章Linux日志的讲解,我们对Linux系统自带的日志服务已经有了一些了解。我们接下来将讲解一些进阶命令&am…

erlang学习:Linux常用命令2

目录操作命令 对目录进行基本操作 相关cd切换目录之类的就直接省去了,以下操作中都会用到 查看当前目录下的所有目录和文件 ls 列表查看当前目录下的所有目录和文件(列表查看,显示更多信息) ls -l 或 ll 在当前目录下创建一个…

高性能并发计数器的比较

参考文档:https://baijiahao.baidu.com/s?id1742540809477784106&wfrspider&forpc 一、常用的并发计数方法 1、synchronized synchronized早期是一个重量级锁,因为线程竞争锁会引起操作系统用户态和内核态切换,浪费资源&#xff…

Element Plus中button按钮相关大全

一、基本用法 使用 type、plain、round 和 circle 来定义按钮的样式。 样式代码如下&#xff1a; <template><div class"mb-4"><el-button>Default</el-button><el-button type"primary">Primary</el-button><el…

C语言常见字符串函数模拟实现一

strlen模拟实现 重点&#xff1a;1.字符串已经\0作为结束标志&#xff0c;strlen返回的是字符串\0前面出现的字符个数&#xff08;不包含\0&#xff09; 2.参数指向的字符串必须要以\0结束。 3.注意函数的返回值是size_t&#xff0c;是无符号的&#xff0c;加减是无法对比的。…

卡西欧相机SD卡格式化后数据恢复指南

在数字摄影时代&#xff0c;卡西欧相机以其卓越的性能和便携性成为了众多摄影爱好者的首选。然而&#xff0c;随着拍摄量的增加&#xff0c;SD卡中的数据管理变得尤为重要。不幸的是&#xff0c;有时我们可能会因为操作失误或系统故障而将SD卡格式化&#xff0c;导致珍贵的照片…

数据类型转换中存在的问题分析

隐式类型转换&#xff08;implicit type conversion&#xff09; 隐式类型转换&#xff08;implicit type conversion&#xff09;包括整型提升&#xff08;integer promotion&#xff09;和标准算数转换&#xff08;usual arithmetic conversions&#xff09; 遵循较大范围优…

堡垒机(Bastion Host)概述

Bastion Host 堡垒机 一、什么是堡垒机&#xff1f; A bastion host is a computer specially designed to mitigate cyberattacks and manage access rights to an internal network. 堡垒机Bastion Host是一种专门设计用于缓解网络攻击并管理内部网络访问权限的计算机。 在…

肖扬新书《微权力下的项目管理》读书笔记2

一个核心思想&#xff1a;“借力” 合格的项目经理是不热衷于培养人的。项目经理的工作场景和职能经理的工作场景往往有很 大不同。职能经理的工作方式通常适用于常态化工作&#xff0c;要有足够的时间去培养人&#xff0c;先把人培 养起来&#xff0c;然后再干事&#xff0c;可…

加油卡APP定制搭建,让加油更便捷!

在汽车时代中&#xff0c;汽车的数量不断增加&#xff0c;加油已经成为了大众生活中不可缺少的一部分。同时&#xff0c;加油卡的出现也为大众的汽车加油提供了更多的优惠方式&#xff0c;为大众节省经济开支&#xff0c;为车主带来便利&#xff1b;同时加油卡的发展也提高了加…

2024年华为杯研赛(E题)数学建模竞赛解题思路|完整代码论文集合

我是Tina表姐&#xff0c;毕业于中国人民大学&#xff0c;对数学建模的热爱让我在这一领域深耕多年。我的建模思路已经帮助了百余位学习者和参赛者在数学建模的道路上取得了显著的进步和成就。现在&#xff0c;我将这份宝贵的经验和知识凝练成一份全面的解题思路与代码论文集合…

如何远程访问局域网内的电脑?局域网内远程桌面怎么实现?揭秘4种干货技巧

想象一下&#xff0c;你正在办公室A&#xff0c;而你想访问办公室B里的某台电脑&#xff0c;却不想起身到另一楼层甚至是另一个房间。 如何不动身就能控制局域网内的另一台电脑呢&#xff1f; 这并不是科幻&#xff0c;而是完全可以通过远程桌面技术来实现。 今天&#xff0…

学习Java(一)类和对象

package demo.ceshi;public class Puppy {private int age;private String name;//构造器public Puppy( String name){this.name name;System.out.println("公主的名字叫&#xff1a;"name);}//设置age的值public void setAge(int age){this.age age;System.out.pr…

智慧仓储-AI销量预测

1、预测系统技术选型 基础层&#xff1a; Hbase、ClickHouse、Hdfs 用来做数据存储 框架层&#xff1a; 以 Spark RDD、Spark SQL、Hive 为主&#xff0c; MapReduce 程序占一小部分&#xff0c;是原先遗留下来的&#xff0c;目前正逐步替换成 Spark RDD。 选择 Spark 除了对…

rsyslogd 内存占用很高解决方案

在Kubernetes&#xff08;K8S&#xff09;集群中&#xff0c;监控日志是非常重要的&#xff0c;而rsyslogd是Linux系统中用于处理系统和应用程序日志的守护进程。有时候rsyslogd可能会占用较高的内存&#xff0c;这时候我们就需要对其进行优化和调整。 阿里云虚拟服务器&…

创客中国AIGC专题赛冠军天鹜科技:AI蛋白质设计引领者

“落霞与孤鹜齐飞,秋水共长天一色——这句出自《滕王阁序》的诗句,是我作为江西人熟记于心的佳句。它描绘的天地壮丽景色常浮现于我的脑海,正是这种豁达与壮观,启发我们将公司命名为‘天鹜科技’,我们希望将源自自然的蛋白质与现代科技的创新精神相结合,打造蛋白质设计与应用的…

16_Python的迭代器

在Python中&#xff0c;迭代是一个非常重要的概念。迭代通常指的是按照某种顺序逐个访问容器中的元素的行为。如使用for循环遍历取值的过程。 可迭代对象&#xff08;Iterable&#xff09; 可迭代对象是任何可以返回一个迭代器的对象。简单来说&#xff0c;它是可以逐一返回其…

机器学习模型中特征贡献度分析:预测贡献与错误贡献

在机器学习领域&#xff0c;特征重要性分析是一种广泛应用的模型解释工具。但是特征重要性并不等同于特征质量。本文将探讨特征重要性与特征有效性之间的关系&#xff0c;并引入两个关键概念&#xff1a;预测贡献度和错误贡献度。 核心概念 预测贡献度&#xff1a;衡量特征在…

【C++】—— stack queue deque

【C】—— stack & queue & deque 1 stack 与 queue 的函数接口2 适配器2.1 发现问题2.2 什么是适配器 3 stack 与 queue的模拟实现3.1 栈的基础框架3.2 栈的模拟实现3.3 队列的模拟实现 4 模板的按需实例化5 deque 的简单介绍5.1 vector 与list对比5.1.1 vector5.1.2 …