要在一个序列里找出第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小元素的更多相关文章

  1. 减治算法之寻找第K小元素问题

    一.问题描写叙述 给定一个整数数列,寻找其按递增排序后的第k个位置上的元素. 二.问题分析 借助类似快排思想实现pation函数.再利用递归思想寻找k位置. 三.算法代码 public static ...

  2. 查询无序列表中第K小元素

    当需要在无需列表中寻找第k小的元素时,一个显然的方法是将所有数据进行排序,然后检索k个元素.这种方法的运行时间为O(n log(n)). 无序列表调用分区函数将自身分解成两个子表,其长度为i和n-i. ...

  3. Ex 2_22 两个有序列表合并后的第k小元素..._第四次作业

    package org.xiu68.ch02; public class Ex2_22 { public static void main(String[] args) { // TODO Auto- ...

  4. 中位数与第K小元素

    算法实际上是模仿快速排序算法设计出来的,其基本思想也是对输入数组进行递归划分,与快速排序不同的是,它只对划分出来的子数组之一进行递归处理: int randompartition(int a[],in ...

  5. 清橙OJ 1082 查找第K小元素 -- 快速排序

    题目地址:http://oj.tsinsen.com/A1082 问题描述 给定一个大小为n的数组s和一个整数K,请找出数组中的第K小元素. 这是一个补充程序的试题,你需要完成一个函数: int fi ...

  6. 快速排序以及第k小元素的线性选择算法

    简要介绍下快速排序的思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此 ...

  7. 算法导论学习之线性时间求第k小元素+堆思想求前k大元素

    对于曾经,假设要我求第k小元素.或者是求前k大元素,我可能会将元素先排序,然后就直接求出来了,可是如今有了更好的思路. 一.线性时间内求第k小元素 这个算法又是一个基于分治思想的算法. 其详细的分治思 ...

  8. (寻找第K小的数&amp;&amp;寻找第K小的数的和)

    这一篇博客以一些OJ上的题目为载体,讲一下寻找第K小的数的方法 方法一: 先将数据排列好,然后,然后return a[k]或者将前K个数加起来 方法二: 基于高速排序.如,一次高速排序将某一个数放到了 ...

  9. 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 ...

随机推荐

  1. android 监听短信数据库,制作短信控制工具,控制别人的手机!!(一)

    序言:本程序示例本着简洁易懂的目的,只做了简单的功能实现,需要用户启动应用,收到短信才有效果.作者将会在后面的(二)篇中加入服务后台运行.自动启动功能,实现一个真正的短信控制工具.本文的目的很简单,让 ...

  2. linux下mysql5.5的安装

    #rpm –qa|grep –i mysql查看已安装的mysql版本 如果有已存在的mysql版本则删除 安装服务端和客户端,去Oracle官网下载: # rpm -ivh MySQL-serve ...

  3. [转] shell文本字符串处理

    第一种方法:#%*,#即截取变量前的字符(左向右截取),%表示截取后面字符(右向左截取),*匹配符 var=foodforthought.jpg ${varible##*string} 从左向右截取最 ...

  4. Ubuntu 修改时区和时间

    tzselect #根据提示选择时区 sudo cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime #把设置好的时区文件粘贴到 /etc/local ...

  5. LINUX更改时区和时间

    因为公司运营海外游戏,服务器也多数放在国外,有时候要求服务器时区和时间要与所服务的地区一致,这里就涉及到更改服务器时区和时间的问题: 下面以一台在美国的服务器为例,我们的IDC提供商是一家德国公司,服 ...

  6. hdu 5105

    题意: y=|a*x^3+b*x^2+c*x+d|    求y的最大值? 题目是bc上的,之前写的时候,没考虑0的情况(太笨了).... 水题吧.... AC代码: #include <iost ...

  7. (转)Repeater在无数据记录时显示暂无数据

    方法就是在FooterTemplate加个Label并根据repeater.Items.Count判断是否有记录.关键代码如下: <FooterTemplate>     <asp: ...

  8. char * 和 void *

    POSIX.1 将 read函数的原型做了修改,经典的定义为 int read(int filedes, char *buf, unsigned nbytes); 修改为 ssize_t read(i ...

  9. PHP Directory 函数

    PHP 5 Directory 函数 函数 描述 chdir() 改变当前的目录. chroot() 改变根目录. closedir() 关闭目录句柄. dir() 返回 Directory 类的实例 ...

  10. 关于Visual Studio中的TraceDebugging文件夹

    最近一段时间发现C盘莫名其妙的变小了,各种清理各种卸载还是没用,电脑慢的是在无法使用 .最后只能一个文件夹一个文件夹的找,最后针对“C:\Documents and Settings\All User ...