线性时间选择(Top K)问题(Java)

简介: 线性时间选择(Top K)问题(Java)

线性时间选择(Top K)问题(Java)


0.png



1、前置介绍

定义

选择问题(select problem)是指在n个元素的集合中,选出某个元素值大小在集合中处于第k位的元素,即所谓的求第k小元素问题(kth-smallest)。


元素选择问题的一般提法


给定具有n个元素的一个线性序集和一个整数k,其中, l<=k<=n ,题目要求找出这n个元素中 第k小 的元素, 即如果将这n 个元素依其线性序排列时,排在第k个的元素即为要找的元素。


易知,

  • k=l 时,就是要找最小元素;
  • k=n 时,就是要找最大元素;
  • k= (n+l)/2 时,称为找中位数。


在某些特殊情况下,很容易设计出解选择问题的线性时间算法。


例如,找n个元素的最小元素和最大元素显然可以在O(n)时间完成。如果 k<=n/logn , 通过堆排序算法可以在O(n+klogn) = O(n)时间内找出第k小元素。 当 k>=n-n/logn 时也一样。


2、分治法求解

一般的选择问题, 特别是中位数的选择问题似乎比找最小元素要难。但事实上, 从渐近阶的意义上看,它们是一样的。

一般的选择问题也可以在OCn) 时间内得到解决。


设原表长度为n,假定经过一趟划分,分成左右两个子表,其中左子表是主元及其左边元素的子表,设其长度为j,右子表是主元右边元素的子表。那么,若 k=j ,则主元就是第k小元素;否则若 k<j ,第k小元素必定在左子表中,需求解的子问题成为在左子表中求第k小元素;若 k>j ,则第k小元素必定在右子表中,需求解的子问题成为在右子表中求第k-j小元素。


下面要讨论解一般的选择问题的分治算法 randomizedSelect 。该算法实际上是 模仿快速排序算法 设计出来的。

其基本思想也是对输入数组进行 递归划分 。与快速排序算法不同的是,它 只对划分出的子数组之一 进行递归处理。


随机选主元算法


假定表中元素各不相同,并且随机选择主元,即在下标区间[left,right]中随机选择一个下标r,以该下标处的元素为主元。


要找数组arr[0:n-1]中第K小元素只要调用randomizedSelect(arr, 0, n -1, k)即可。


具体算法可描述如下:

privatestaticComparablerandomizedSelect(intp,intr,intk) {
if (p==r) returna[p];
inti=randomizedpartition(p,r),
j=i-p+1;
if (k<=j) {
returnrandomizedSelect(p,i,k);
    } else {
returnrandomizedSelect(i+1,r,k-j);
    }
}


3、代码实现


  • 将n个输入元素划分成n/5个组,每组5个元素,只可能有一个组不是5个元素。用任意一种排序算法,将每组中的元素排好序,并取出每组的中位数,共n/5个。


  • 递归调用select来找出这n/5个元素的中位数。如果n/5是偶数,就找它的2个中位数中较大的一个。以这个元素作为划分基准。


1.png


例子


1.png


解题步骤


(1)把前面25个元素分为5(=29/5)组:

(8,31,60,33,17),(4,51,57,49,35),(11,43,37,3,13),(52,6,19,25,32),(54,16,5,41,7)


(2)提取每一组的中值元素,构成集合{31,49,13,25,16};


(3)递归地使用算法求取该集合的中值,得到m=25:


(4)根据m=25,把29个元素划分为3个子数组:


  • P = {8,17,4,11,3,13,6,19,16,5,7,23,22}
  • Q = {25}
  • R = {31,60,33,51,57,49,35,43,37,52,32,54,41,46,29}


(5)由于P=13、Q=1、k=18,所以放弃P、Q,使k=18-13-1=4,对R递归地执行本算法:


(6)将R划分成3(⌊18/5)组:{31,60,33,51,57},{49,35,43,37,52},{32,54,41,46,29}


(7)求取这3组元素的中值元素分别为:{51,43,41},这个集合的中值元素是43;


(8)根据43将R划分成3组:

{31,33,35,37,32,41,29},{43},{60,51,57,49,52,54,46}


(9)因为k=4,第一个子数组的元素个数大于k,所以放弃后面两个子数组,以k=4对第一个子

数组递归调用本算法;


(10)将这个子数组分成5个元素的一组:{31,33,35,37,32},取其中值元素为33:


(11)根据33,把第一个子数组划分成{31,32,29},{33},{35,37,41}


(12)因为k=4,而第一、第二个子数组的元素个数为4,所以33即为所求取的第18个小元素。



privatestaticComparableselect (intp, intr, intk) {
if (r-p<5) {  // 如果元素个数小于5,可以直接返回结果// 用某个简单排序算法对数组a[p:r]排序;bubbleSort(p, r);
returna[p+k-1];
      }
// 将a[p + 5 * i]至a[p + 5 * i + 4]的第3小元素// 与a[p+i]交换位置;// 找中位数的中位数,r-p-4即上面所说的n-5for (inti=0; i<= (r-p-4) /5; i++) {
ints=p+5*i, t=s+4;
for (intj=0; j<3; j++) {
bubble(s, t-j);
         }
MyMath.swap(a, p+i, s+2);
      }
Comparablex=select(p, p+ (r-p-4) /5, (r-p+6)/10);
inti=partition(p,r,x), j=i-p+1;
if (k<=j) {
returnselect(p,i,k);
      } else {
returnselect(i+1,r,k-j);
      }
}


4、复杂度分析

在最坏情况下,算法randomizedSelect需要O(n2)计算时间(在找最小元素时,总是在最大元素处划分)


但可以证明,算法randomizedSelect可以在O(n)平均时间内找出n个输入元素中的第k小元素。


如果能在线性时间内找到一个划分基准,使得按这个基准所划分出的2个子数组的长度都至少为原数组长度的ε倍(0<ε<1是某个正常数),那么就可以在最坏情况下用O(n)时间完成选择任务。



例如,若ε=9/10,算法递归调用所产生的子数组的长度至少缩短1/10。所以,在最坏情况下,算法所需的计算时间T(n)满足递归式T(n)≤T(9n/10)+O(n) 。


由此可得T(n)=O(n)。

2.png


设所有元素互不相同。在这种情况下,找出的基准x至少比3(n-5)/10个元素大,因为在每一组中有2个元素小于本组的中位数,而n/5个中位数中又有(n-5)/10个小于基准x。同理,基准x也至少比3(n-5)/10个元素小。而当n≥75时,3(n-5)/10≥n/4所以按此基准划分所得的2个子数组的长度都至少缩短1/4。(备注:就是说明递归子问题规模是下降的,划分后的两个子数组分别至多有3n/4个元素)


3.png


上述算法将每一组的大小定为5,并选取75作为是否作递归调用的分界点。这2点保证了T(n)的递归式中2个自变量之和n/5+3n/4=19n/20=εn,0<ε<1。这是使T(n)=O(n)的关键之处。当然,除了5和75之外,还有其他选择。


递归树

4.jpeg


5、扩展


分组为什么5个元素一组?3个一组或7个一组行不行?


分析:递归调用


1、求x的工作量与中位数集合的规模有关,其值=n/t有关,t为每组元素数,t越大,其规模越小


2、规约后子问题大小与分组元素数t有关,t越大,子问题规模大。

5e.jpeg


6a.jpeg

6、参考资料

  • 算法分析与设计(第四版)
目录
相关文章
|
Java
java判断当前时间是否在某个时间区间内(可精确到毫秒)
java判断当前时间是否在某个时间区间内(可精确到毫秒)
690 0
java判断当前时间是否在某个时间区间内(可精确到毫秒)
|
搜索推荐 Java
Java基础数组-选择排序算法
Java基础数组-选择排序算法
Java基础数组-选择排序算法
|
Java 程序员
Java基础if选择01
Java基础if选择01
|
Java
Java中格林尼治时间和时间戳的相互转换
Java中格林尼治时间和时间戳的相互转换
679 0
Java将CST的时间字符串转换成需要的日期格式字符串
Java将CST的时间字符串转换成需要的日期格式字符串
|
Java
java时间换算(BJU转UTC)
UTC是世界协调时,BJT是北京时间,UTC时间相当于BJT减去8。现在,你的程序要读入一个整数,表示BJT的时和分。整数的个位和十位表示分,百位和千位表示小时。如果小时小于10,则没有千位部分;如果小时是0,则没有百位部分;如果分小于10分,需要保留十位上的0。如1124表示11点24分,而905表示9点5分,36表示0点36分,7表示0点7分。
220 0
java时间换算(BJU转UTC)
JAVA 实现五种基本排序方法的实现以及时间的统计
JAVA 实现五种基本排序方法的实现以及时间的统计
java获取时间间隔,获取当天每隔15分钟的时间
Java开发中日常遇到的关于时间的问题
java获取时间间隔,获取当天每隔15分钟的时间