寻找第K小元素
要在一个序列里找出第K小元素,可以用排序算法,然后再找。可以证明,排序算法的上界为O(nlogn)。
在这里,给出两种可以在线性时间内找出第K小元素的方法。
方法1:
(1) 选定一个比较小的阈值(如44),当序列元素小于阈值时,直接用排序算法来做;
(2) 当序列元素大于阈值时,把元素划分为以5个元素为一组,每一组元素自身作排序,然后挑出每一组元素的中间值,再在所有的中间值中,递归调用本算法,挑出中间值,可以认为,此值大约为整个序列的中间值(当序列元素个数不是5的倍数时,最后一组不足5的舍掉,这个对中间值影响不大);

(图片参考《ALGORITHMS DESIGN TECHNIQUES AND ANALYSIS》 M. H. Alsuwaiyel)
(3) 把元素按中间值划分为三组,第一组小于中间值,第二组等于中间值,第三组大于中间值;
(4) 若第一组的元素个数大于等于K,即第K个元素在第一组内;若第一组和第二组的元素个数大于等于K,即中间值为第K个元素;否则,第K个元素在第三组,再递归调用本算法,注意K要减去一二组的元素个数。
public static int select(int[] A, int k){
return selectDo(A, 0, A.length-1, k);
}
private static int selectDo(int[] A, int low, int high, int k){
//select k min number
int p = high - low + 1;
if(p < 44){
Arrays.sort(A, low, high+1);
return A[low+k];
}
//A divided into q groups, each group 5 elements, and sort them
int q = p/5;
int[] M = new int[q];
for(int i = 0; i < q; i ++){
Arrays.sort(A, low + 5*i, low + 5*i + 5);
M[i] = A[low+5*i+2];
}
//select mid in M
int mid = selectDo(A, 0, q-1, (q-1)/2);
//A divided into 3 groups
int[] A1 = new int[p];
int[] A2 = new int[p];
int[] A3 = new int[p];
int count1, count2, count3;
count1 = count2 = count3 = 0;
for(int i = low; i <= high; i ++){
if(A[i] < mid)
A1[count1++] = A[i];
else if(A[i] == mid)
A2[count2++] = A[i];
else
A3[count3++] = A[i];
}
if(count1 >= k)
return selectDo(A1, 0, count1-1, k);
if(count1 + count2 >= k)
return mid;
return selectDo(A3, 0, count3-1, k-count1-count2);
}
Java
这个方法虽然可以以最坏时间复杂度O(n),但是系数值会比较大,而且算法比较复杂。
方法2:
(1) 随机挑选一个元素X作为数组的划分,此时数组分为三部分,第一部分是小于X的元素,第二部分只有一个元素就是X,第三部分是大于或等于X的元素;
(2) 当第一部分的元素个数N大于K时,说明第K个元素在第一部分;当第一部分的元素个数N等于K时,说明X就是第K个元素(注意到下标从0开始);否则,第K个元素在第三部分,再递归调用本算法,注意K要减去第一部分的元素个数再减1(包括X)。
public static int randomSelect(int[] A, int k){
return randomSelectDo(A, 0, A.length-1, k);
}
private static int randomSelectDo(int[] A, int low, int high, int k){
int i = randomPartition(A, low, high);
//n is the number of < A[i]
int n = i-low;
if(n > k)
return randomSelectDo(A, low, i-1, k);
else if(n == k)
return A[i];
else
return randomSelectDo(A, i+1, high, k-n-1);
}
private static void swap(int[] A, int i, int j){
int temp = A[i];
A[i] = A[j];
A[j] = temp;
}
private static int randomPartition(int[] A, int low, int high){
//random divide
Random rand = new Random();
int r = rand.nextInt(high-low+1) + low;
swap(A, low, r);
int i = low;
int x = A[low];
for(int j = low+1; j <= high; j ++){
if(A[j] < x){
i ++;
if(i != j){
swap(A, i, j);
}
}
}
swap(A, low, i);
return i;
}
Java
此方法和快速排序有点相似,也是需要划分数组的方法;与方法1相比,不同之处在于划分元素的选择。采用随机化划分,在实际上可以达到O(n)的要求。而且算法简洁优美。
寻找第K小元素的更多相关文章
- 减治算法之寻找第K小元素问题
一.问题描写叙述 给定一个整数数列,寻找其按递增排序后的第k个位置上的元素. 二.问题分析 借助类似快排思想实现pation函数.再利用递归思想寻找k位置. 三.算法代码 public static ...
- 查询无序列表中第K小元素
当需要在无需列表中寻找第k小的元素时,一个显然的方法是将所有数据进行排序,然后检索k个元素.这种方法的运行时间为O(n log(n)). 无序列表调用分区函数将自身分解成两个子表,其长度为i和n-i. ...
- Ex 2_22 两个有序列表合并后的第k小元素..._第四次作业
package org.xiu68.ch02; public class Ex2_22 { public static void main(String[] args) { // TODO Auto- ...
- 中位数与第K小元素
算法实际上是模仿快速排序算法设计出来的,其基本思想也是对输入数组进行递归划分,与快速排序不同的是,它只对划分出来的子数组之一进行递归处理: int randompartition(int a[],in ...
- 清橙OJ 1082 查找第K小元素 -- 快速排序
题目地址:http://oj.tsinsen.com/A1082 问题描述 给定一个大小为n的数组s和一个整数K,请找出数组中的第K小元素. 这是一个补充程序的试题,你需要完成一个函数: int fi ...
- 快速排序以及第k小元素的线性选择算法
简要介绍下快速排序的思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此 ...
- 算法导论学习之线性时间求第k小元素+堆思想求前k大元素
对于曾经,假设要我求第k小元素.或者是求前k大元素,我可能会将元素先排序,然后就直接求出来了,可是如今有了更好的思路. 一.线性时间内求第k小元素 这个算法又是一个基于分治思想的算法. 其详细的分治思 ...
- (寻找第K小的数&&寻找第K小的数的和)
这一篇博客以一些OJ上的题目为载体,讲一下寻找第K小的数的方法 方法一: 先将数据排列好,然后,然后return a[k]或者将前K个数加起来 方法二: 基于高速排序.如,一次高速排序将某一个数放到了 ...
- Coursera Algorithms week3 快速排序 练习测验: Selection in two sorted arrays(从两个有序数组中寻找第K大元素)
题目原文 Selection in two sorted arrays. Given two sorted arrays a[] and b[], of sizes n1 and n2, respec ...
随机推荐
- HDFS集群balance(4)-- 测试计划
转载请注明博客地址:http://blog.csdn.net/suileisl HDFS集群balance,对应版本balance design 6 如需word版本,请QQ522173163联系索要 ...
- ndroid网络(4):HttpClient必经之路----使用线程安全的单例模式HttpClient,及HttpClient和Application的融合
上文简 单介绍了HttpClient和Tomcat服务器的交互,主角是HttpClient,然后它跟服务器交互有两种方式即get和post.所以这个 HttpClient就类似于电脑上用的浏览器.当我 ...
- Java 编程的动态性,第 7 部分: 用 BCEL 设计字节码--转载
在本系列的最后三篇文章中,我展示了如何用 Javassist 框架操作类.这次我将用一种很不同的方法操纵字节码——使用 Apache Byte Code Engineering Library (BC ...
- [转] Console命令详解,让调试js代码变得更简单
http://www.cnblogs.com/see7di/archive/2011/11/21/2257442.html Firebug是网页开发的利器,能够极大地提升工作效率. 但是,它不太容易上 ...
- StarUML中时序图添加小人
转载于 http://blog.csdn.net/longyuhome/article/details/9011629 在看时序图的例子的时候,发现有些的时序图上有小人的图标,可是一些UML工具却没有 ...
- Creating Lists and Cards 创建列表和卡片
To create complex lists and cards with material design styles in your apps, you can use the Recycler ...
- 用timer控件实现sleep效果
有时候我们需要代码延迟执行,这就需要用到Thread.Sleep()这个方法,但这个方法在主线程使用时会造成界面假死.使用timer控件既能达到代码延迟执行的效果,又不会有假死的困扰. 假设我们需要在 ...
- DedeCMS批量替换栏目文件保存目录的方法
学点sql还是很有必要的. 有时候由于栏目太多,但是要修改一下栏目的保存目录.一个一个修改真的有点费事和慢.所以想了一个方法来批量修改栏目的保存目录.就是批量替换: update dede_arc ...
- javascript 更改控件的class.
指定 className即可, 如通过id,可这样更改: document.getElementById("myDIV").className = "calssName& ...
- JavaScript 客户端JavaScript之 脚本化浏览器窗口
1.计时器 客户端Javascript以全局函数setTimeOut().clearTimeOut().setInterval().clearInterval()提供这一功能. 前者是从运行的那一 ...