云漫圈 | 寻找无序数组的第k大元素
戳蓝字“CSDN云计算”关注我们哦!
作者:小灰
来源:程序员小灰
本期封面作者:泰勒太乐
————— 第二天 —————
题目是什么意思呢?比如给定的无序数组如下:
如果 k=6,也就是要寻找第6大的元素,这个元素是哪一个呢?
显然,数组中第一大的元素是24,第二大的元素是20,第三大的元素是17 ...... 第6大的元素是9。
方法一:排序法
这是最容易想到的方法,先把无序数组从大到小进行排序,排序后的第k个元素,自然就是数组中的第k大元素。
方法二:插入法
维护一个长度为k的数组A的有序数组,用于存储已知的k个较大的元素。
接下来遍历原数组,每遍历到一个元素,和数组A中最小的元素相比较,如果小于等于数组A的最小元素,继续遍历;如果大于数组A的最小元素,则插入到数组A中,并把曾经的最小元素“挤出去”。
比如k=3,先把最左侧的7,5,15三个数有序放入数组A当中,代表当前最大的三个数。
这时候,遍历到3, 由于3<5,继续遍历。
接下来遍历到17,由于17>5,插入到数组A的合适位置,类似于插入排序,并把原先最小的元素5“挤出去”。
继续遍历原数组,一直遍历到数组的最后一个元素......
最终,数组A中存储的元素是24,20,17,代表着整个数组中最大的3个元素。此时数组A中的最小的元素17就是我们要寻找的第k大元素。
————————————
什么是二叉堆?不太了解的小伙伴可以先看看这一篇:漫画:什么是二叉堆?
简而言之,二叉堆是一种特殊的完全二叉树,它包含大顶堆和小顶堆两种形式。
其中小顶堆的特点,是每一个父节点都大于等于自己的子节点。要解决这个算法题,我们可以利用小顶堆的特性。
方法三:小顶堆法
维护一个容量为k的小顶堆,堆中的k个节点代表着当前最大的k个元素,而堆顶显然是这k个元素中的最小值。
遍历原数组,每遍历一个元素,就和堆顶比较,如果当前元素小于等于堆顶,则继续遍历;如果元素大于堆顶,则把当前元素放在堆顶位置,并调整二叉堆(下沉操作)。
遍历结束后,堆顶就是数组的最大k个元素中的最小值,也就是第k大元素。
假设k=5,具体的执行步骤如下:
1.把数组的前k个元素构建成堆。
2.继续遍历数组,和堆顶比较,如果小于等于堆顶,则继续遍历;如果大于堆顶,则取代堆顶元素并调整堆。
遍历到元素2,由于 2<3,所以继续遍历。
遍历到元素20,由于 20>3,20取代堆顶位置,并调整堆。
遍历到元素24,由于 24>5,24取代堆顶位置,并调整堆。
以此类推,我们一个一个遍历元素,当遍历到最后一个元素8的时候,小顶堆的情况如下:
3.此时的堆顶,就是堆中的最小值,也就是数组中的第k大元素。
这个方法的时间复杂度是多少呢?
1.构建堆的时间复杂度是 O(k)
2.遍历剩余数组的时间复杂度是O(n-k)
3.每次调整堆的时间复杂度是 O(logk)
其中2和3是嵌套关系,1和2,3是并列关系,所以总的最坏时间复杂度是O((n-k)logk + k)。当k远小于n的情况下,也可以近似地认为是O(nlogk)。
这个方法的空间复杂度是多少呢?
刚才我们在详细步骤中把二叉堆单独拿出来演示,是为了便于理解。但如果允许改变原数组的话,我们可以把数组的前k个元素“原地交换”来构建成二叉堆,这样就免去了开辟额外的存储空间。
因此,方法的空间复杂度是O(1)。
/**
* 寻找第k大的元素
* @param array 待调整的堆
* @param k 第几大
*/
public static int findNumberK(int[] array, int k){
//1.用前k个元素构建小顶堆
buildHeap(array, k);
//2.继续遍历数组,和堆顶比较
for(int i=k; i<array.length;i++){
if(array[i] > array[0]){
array[0] = array[i];
downAdjust(array, 0, k);
}
}
//3.返回堆顶元素
return array[0];
}
/**
* 构建堆
* @param array 待调整的堆
* @param length 堆的有效大小
*/
private static void buildHeap(int[] array, int length) {
// 从最后一个非叶子节点开始,依次下沉调整
for (int i = (length-2)/2; i >= 0; i--) {
downAdjust(array, i, length);
}
}
/**
* 下沉调整
* @param array 待调整的堆
* @param index 要下沉的节点
* @param length 堆的有效大小
*/
private static void downAdjust(int[] array, int index, int length) {
// temp保存父节点值,用于最后的赋值
int temp = array[index];
int childIndex = 2 * index + 1;
while (childIndex < length) {
// 如果有右孩子,且右孩子小于左孩子的值,则定位到右孩子
if (childIndex + 1 < length && array[childIndex + 1] < array[childIndex]) {
childIndex++;
}
// 如果父节点小于任何一个孩子的值,直接跳出
if (temp <= array[childIndex])
break;
//无需真正交换,单向赋值即可
array[index] = array[childIndex];
index = childIndex;
childIndex = 2 * childIndex + 1;
}
array[index] = temp;
}
public static void main(String[] args) {
int[] array = new int[] {7,5,15,3,17,2,20,24,1,9,12,8};
System.out.println(findNumberK(array, 5));
}
方法四:分治法
大家都了解快速排序,快速排序利用分治法,每一次把数组分成较大和较小的两部分。
我们在寻找第k大元素的时候,也可以利用这个思路,以某个元素A为基准,把大于于A的元素都交换到数组左边,小于A的元素都交换到数组右边。
比如我们选择以元素7作为基准,把数组分成了左侧较大,右侧较小的两个区域,交换结果如下:
包括元素7在内的较大元素有8个,但我们的k=5,显然较大元素的数目过多了。于是我们在较大元素的区域继续分治,这次以元素12位基准:
这样一来,包括元素12在内的较大元素有5个,正好和k相等。所以,基准元素12就是我们所求的。
这就是分治法的大体思想,这种方法的时间复杂度甚至优于小顶堆法,可以达到O(n)。有兴趣的小伙伴可以尝试用代码实现一下。
1.微信群:
添加小编微信:color_ld,备注“进群+姓名+公司职位”即可,加入【云计算学习交流群】,和志同道合的朋友们共同打卡学习!
2.征稿:
投稿邮箱:liudan@csdn.net;微信号:color_ld。请备注投稿+姓名+公司职位。
推荐阅读
有问有答 | 分布式服务框架精华问答
深入理解与应用Hadoop中的MapReduce
等了20年的物联网,这次真的会成功吗?
云要闻 | 腾讯在华南建云计算基地;致敬Larry Roberts
网友们票选的2018 Best Paper,你pick谁?
程序员有话说 | 大专生毕业 6 年月薪 3W+:不从众也不普通
Windows 10 终于干掉了 Windows 7!
比特币拒绝第340次“被死亡”
程序员抢票的正确姿势 ↓↓交朋友还能抢票?
为交流学习,请备注抢票+姓名+公司职位
点击“阅读原文”,打开 CSDN App 阅读更贴心!
喜欢就点击“好看”吧!
云漫圈 | 寻找无序数组的第k大元素相关推荐
- 从当前元素继续寻找_云漫圈 | 寻找无序数组的第k大元素
戳蓝字"CSDN云计算"关注我们哦! 作者:小灰 来源:程序员小灰 本期封面作者:泰勒太乐 ----- 第二天 ----- 题目是什么意思呢?比如给定的无序数组如下: 如果 k ...
- 在一个无序的int数组上构建一个最小堆的时间复杂度_漫画:寻找无序数组的第k大元素(修订版)...
----- 第二天 ----- 题目是什么意思呢?比如给定的无序数组如下: 如果 k=6,也就是要寻找第6大的元素,这个元素是哪一个呢? 显然,数组中第一大的元素是24,第二大的元素是20,第三大的元 ...
- 数组中的元素赋值给元素_漫画:寻找无序数组的第k大元素
本期封面作者:泰勒太乐 ----- 第二天 ----- 题目是什么意思呢?比如给定的无序数组如下: 如果 k=6,也就是要寻找第6大的元素,这个元素是哪一个呢? 显然,数组中第一大的元素是24,第 ...
- 遍历数组是什么意思_漫画:寻找无序数组的第k大元素(修订版)
----- 第二天 ----- 题目是什么意思呢?比如给定的无序数组如下: 如果 k=6,也就是要寻找第6大的元素,这个元素是哪一个呢? 显然,数组中第一大的元素是24,第二大的元素是20,第三大的元 ...
- 寻找无序数组中第k大的数
对于一个无序的数组,怎样找到其中第k大的数呢?下面总结几种方法. 1.直接排序法 使用常见的归并排序.堆排序等算法对数组进行排序,然后找到第k大的数.排序算法的时间复杂度为O(nlogn),所以算法总 ...
- 求无序数组的第K(大/小)数的三种方法
题目描述:给定一个无序数组(长度为n)和一个正整数 K,让你求此数组中第K大的数 方法1:最简单的思路,你肯定会想到 先把数组排序,然后再取下标为K的对应数组元素.这种方法肯定是可行的,但是这种方法无 ...
- 数组中第K大元素(java多种方式实现)
题目描述: Leetcode215题:在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5 ...
- 无序数组求第k大的数 python_整数无序数组求第K大数
import java.util.Scanner; /** * 类似与求第k小的问题 * 求第k大相当于求第n-k+1小,n为数组长度 * * 著名的BFPRT算法可保证在线性时间内得到结果. * h ...
- 算法设计--查找无序数组中第K大的数字
给出一个数组,要求查找出这个数组中按顺序排第K大的数字. 思路:利用快速排序的思想,把数组中的元素分治,并计算中间值的位置,如果是K,则这个数就是第K大的数字,如果比K小,那么再次利用快速排序分治中值 ...
最新文章
- java控制台next_java读取控制台输入以及next() ,nextLine(), nextInt() 和nextDouble()的比较...
- Permission is only granted to system app
- ASP.NET运行环境配置
- Serverless Devs 2.0 开箱测评:Serverless 开发最佳实践
- 判断字符串出现次数最多的字符 及 次数
- C++实现线段树(lazy-tag方法)-区间修改,区间查询
- Windows Mobile开发应该选择哪种开发语言?
- C++模板学习:函数模板、结构体模板、类模板
- spring整合mybatis的坑
- MySQL如何用一条SQL将一张表里的数据插入到另一张表
- 完整的Socket代码
- 机器学习基础(二十三)—— 概念、定义
- 凸优化学习笔记(五):凸优化算法、无约束优化算法、有约束优化算法
- unity3d面试题
- java读取配置文件中文乱码
- git did not exit cleanly exit code 128
- TinyXml 介绍
- mysql zip 版本_MySQL zip版本安装
- 不用发论文也不教课的12名教授,为这个研究所拿下8次学界最高奖
- (C#)一个简易的水准网平差程序
热门文章
- redis关键字删除_微信公众号文章防删除
- hp服务器raid一直显示同步,HP Netserver NetRAID 一致性检查常见问题解答(FAQ)
- jeecg 导出的excel不能使用公式_微软:Excel公式是世界上使用最广泛的编程语言...
- 网友的有趣发现:冬天里,欧洲古建筑上的雕像都好像“生病了”
- 谷歌AI算法 助力可控核聚变研究
- 21天早起计划,奖你1000+元!
- 现代中国第一位数学博士是谁?
- pytorch深度学习之音频librosa库与torchaudio库的安装与使用(windows和ubuntu)
- C/C++程序员必须熟悉的开源库
- Java Map中那些巧妙的设计