寻找第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 ...
随机推荐
- java.lang.UnsatisfiedLinkError: Native method not found 三种可能解决方案
so文件编译生成后,运行时,有时候会遇到java.lang.UnsatisfiedLinkError: Native method not found问题,有可能是以下三种因素: 一.Jni方法头部大 ...
- 使用一个小图片tile平铺到ImageView中或Activity背景
方法两种: 首先必须在res/drawable目录下包含一个background.jpg 方法1:在res/drawable中创建一个xml文件(background_repeat.xml) 内容为 ...
- Everything 使用技巧
使用技巧 可以使用逻辑符空格(与), |(或),!(非),及通配符 * 和 ? ,符号或搜索内容之间必须加空格且空格任意使用如果经常搜索同一内容,可以使用书签功能,且可以对书签进行添加.编辑.排序.导 ...
- bzoj 3043 (差分序列运用)
维护差分序列 显然要使差分序列的后n-1位为0 对于原来的区间操作 只需要单点修改或者两个点修改 就转化成了 对于差分序列但以一个数+ 或 - 或者一个+1同时一个- ans1=max(sum1,su ...
- N!水题
//题目是求N!的问题,思路:设定一个整形数组来存放每次计算过后的值 有两个for循环,第一个for循环每次加进一个数 然后在第二个for循环里面计算出此时的阶乘,比如9999,先给出i=2 在第二个 ...
- asp.net中应用JQuery.pagination分页
JQuery.pagination这款分页控件非常好用,可实现无刷新分页,为了方便下次做项目便于拷贝,所以在此发布一下,具体的实现流程如下: 效果图: JQuery.pagination的各个参数的说 ...
- rpm命令数据库修复日志
今天在linux安装软件过程中遇到了一个小坑,rpm数据库被破坏: 状况: #rpm -qa | grep rpm 返回: [解决方案] 删除旧数据库,然后重建数据库: 删除旧数据库: # rm /v ...
- JAVA-3-水仙花
public static void main(String[] args) { // TODO 自动生成的方法存根 int i = 100; while (i < 1000) { int a, ...
- hdu 2480 贪心+简单并查集
Steal the Treasure Time Limit: 10000/6000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Othe ...
- Helloworld和程序员人生
转:Helloworld和程序员人生 高中时期 10 PRINT "HELLO WORLD" 20 END 大学新生 program Hello(input, output) be ...