寻找数组中的第K大的元素,多种解法以及分析
遇到了一个很简单而有意思的问题,可以看出不同的算法策略对这个问题求解的优化过程。
问题:寻找数组中的第K大的元素。
最简单的想法是直接进行排序,算法复杂度是O(N*logN)。这么做很明显比较低效率,因为不要求别的信息只要计算出第K大的元素。当然,如果在某种情况下需要频繁访问第K大的元素就可以先进行一次排序在直接得出结果。
第一种方式是这样,用选择排序,冒泡法,或者交换排序这类的排序,对前K个元素进行排序。这三种算法也许不是最快的排序算法。但是都有个性质:计算出最大(小)的元素的算法复杂度是O(N)。这个过程不能中断,要计算第三大的元素必须建立在已经算出第二大的元素的基础上(因为每次都是计算当前数组最大)。所以它的算法复杂度是O(N*K);
第二种方法是用快速排序的思想。快速排序每次把一个元素交换到正确的位置,同时把左边的都方上大的,右边都放上小的。这个算法每一次选取一个枢纽元,排序之后,查看枢纽元的位置。如果它的位置大于K,就说明,要求出前面一个子序列的第K大的元素。反之,如果小于K,就说明要求出在后面一个序列的第K - 前一个序列的长度个元素。
如此,就把这个问题改变成了一个可以用快排思想解决的问题。对于快速排序,算法复杂度是O(N*logN)。而这个算法的算法复杂度是O(N)。为什么呢?
其实这个地方的算法复杂度分析很有意思。第一次交换,算法复杂度为O(N),接下来的过程和快速排序不同,快速排序是要继续处理两边的数据,再合并,合并操作的算法复杂度是O(1),于是总的算法复杂度是O(N*logN)(可以这么理解,每次交换用了N,一共logN次)。但是这里在确定枢纽元的相对位置(在K的左边或者右边)之后不用再对剩下的一半进行处理。也就是说第二次插入的算法复杂度不再是O(N)而是O(N/2),这不还是一样吗?其实不一样,因为接下来的过程是1+1/2+1/4+........ < 2,换句话说就是一共是O(2N)的算法复杂度也就是O(N)的算法复杂度。
这个算法目前我在数据结构和算法书上和剑指Offer上都看到过。算是一种很经典很经典的算法。原因是因为他通过努力把算法复杂度在每次递归中下降一些,最终让整个算法的复杂度下降极多,算是一种十分聪明的做法。
第三种方法很是简单,但是使用它需要某个条件,也就是输入数组的取值范围很小,最好的情况是能形成完全分布,也就是1000大小的数组里面的数字是从1到1000这样子。首先,生成一个能够完全装下原数组的数组,这个地方的装下是指数组大小等于原数组最大元素(也许还有优化,但这么描述简单一点),比如原数组是[1,2,3,4,5],我要生成的数组大小是5,如果原数组是[5,3,6,10],我要生成的数组大小是10。接下来遍历原数组,把每一个元素放到第二个数组对应的下标处,5就放在下标为5的地方(实际过程中要减1,因为是数组从0开始)。放的过程中增加元素值用来统计这个元素出现的次数。这一过程算法复杂度是O(N)。接下来,再遍历生成的数组,找出第K大的元素。
这个过程的算法复杂度是多少呢?其实这个和原数组很有关系,原数组越离散也就越糟糕。比如原数组是[1,1000],这样就十分糟糕。第二部的算法复杂度是O(M),M是前数组的最大值。总的算法复杂度O(N)+O(M);
由此可见第三种方法在这个问题的处理非常不好。虽然第三种方法限制颇多(浮点型和负数还有对原数组大小的要求),但是第三种方法的实质是一种散列。就是把原来的映射关系变成了一种反映射。也就是说如果形成了数据与地址的直接映射。但是这种映射的问题也体现的很明显,它这么做也只能算是捡了个漏子,如果输入数组稍微一边,还是一样要用hash算法计算其hash值。再把hash值映射到地址上。
第四种方法是用二叉堆来做。对大小为N的数组构建二叉堆的算法复杂度是O(N)。然后每次下滤的算法复杂度是O(logN),一共下滤K次,算法复杂度是O(N+K*logN)。
这种做法比较适合用来处理输入数组极大的情况,原因是如果输入数组大到不能放入内存,那么构建二叉堆(优先队列)的时候就可以只构造一个K个元素的优先队列。如果下一个元素比这个最小堆的堆顶还小就直接pass。第二个原因是算法二在对付一个极大的输入队列的时候算法复杂度的一个常数会很大。
寻找数组中的第K大的元素,多种解法以及分析的更多相关文章
- 如何寻找无序数组中的第K大元素?
如何寻找无序数组中的第K大元素? 有这样一个算法题:有一个无序数组,要求找出数组中的第K大元素.比如给定的无序数组如下所示: 如果k=6,也就是要寻找第6大的元素,很显然,数组中第一大元素是24,第二 ...
- 记录我对'我们有成熟的时间复杂度为O(n)的算法得到数组中任意第k大的数'的误解
这篇博客记录我对剑指offer第2版"面试题39:数组中出现次数超过一半的数字"题解1的一句话的一个小误解,以及汇总一下涉及partition算法的相关题目. 在剑指offer第2 ...
- [LeetCode] Kth Largest Element in a Stream 数据流中的第K大的元素
Design a class to find the kth largest element in a stream. Note that it is the kth largest element ...
- LeetCode:数组中的第K个最大元素【215】
LeetCode:数组中的第K个最大元素[215] 题目描述 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: ...
- LeetCode215. 数组中的第K个最大元素
215. 数组中的第K个最大元素 问题描述 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 示例 1: 输入: [3 ...
- LeetCode 215——数组中的第 K 个最大元素
1. 题目 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5,6,4] 和 k = 2 ...
- Leetcode 215.数组中的第k个最大元素
数组中的第k个最大元素 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5,6,4] 和 ...
- Leetcode题目215.数组中的第K个最大元素(中等)
题目描述: 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5,6,4] 和 k = 2 ...
- 215. 数组中的第K个最大元素 + 快速排序 + 大根堆
215. 数组中的第K个最大元素 LeetCode-215 另一道类似的第k大元素问题:https://www.cnblogs.com/GarrettWale/p/14386862.html 题目详情 ...
随机推荐
- java 24 - 9 GUI 之 给窗体换图标、设置启动在屏幕中间、更换皮肤
A.首先更改窗体左上角的图片 步骤一: 创建3个包,分别建立1个类 第一个是窗体的包,窗体类:设置窗体的主要布置和功能 第二个是资源包,图片:把想要改的图案拉进来 第三个是UI界面包,UI界面设计类: ...
- Windows 7 USB DVD Download Tool
1.下载.安装.运行Windows 7 USB DVD Download Tool: 百度官方下载 http://rj.baidu.com/soft/detail/20458.html 2.点击&qu ...
- curl命令
定位后端接口是否ok,经常使用到curl -b/cookie <name=string/file> cookie字符串或文件读取位置 curl http://localhost --co ...
- noi题库(noi.openjudge.cn) 1.7编程基础之字符串T31——T35
T31 字符串P型编码 描述 给定一个完全由数字字符('0','1','2',-,'9')构成的字符串str,请写出str的p型编码串.例如:字符串122344111可被描述为"1个1.2个 ...
- 谈谈软件项目的dependency
说到软件项目的依赖管理,可以从三个方面来考虑: 一.由build system控制的dependency 现在的build system,都支持一定程度上的dependency management, ...
- C#中字典集合HashTable、Dictionary、ConcurrentDictionary三者区别
C#中HashTable.Dictionary.ConcurrentDictionar三者都表示键/值对的集合,但是到底有什么区别,下面详细介绍 一.HashTable HashTable表示键/值对 ...
- C#开发Windows服务
Microsoft Windows 服务(即,以前的 NT 服务)使您能够创建在它们自己的 Windows 会话中可长时间运行的可执行应用程序. 服务可以在计算机启动时自动启动,可以暂停和重新启动而且 ...
- List<T>与Dictionary<string,T>频繁检索的性能差距
一直对LINQ简洁高效的语法青睐有加,对于经常和资料库,SQL语法打交道的C#开发者来说,LINQ无疑是一个非常不错的选择,当要在List<T>(T为一个普通对象)集合中查找满足某些条件的 ...
- 三言两语聊Python模块–文档测试模块doctest
doctest是属于测试模块里的一种,对注释文档里的示例进行检测. 给出一个例子: splitter.pydef split(line, types=None, delimiter=None): &q ...
- win8/8.1 免密码登录设置
http://www.ehow.com/how_8013338_start-windows-password.html 原文见上方链接 1,win+r调出命令输入窗口,输入 netplwiz 回车 2 ...