Top k问题(线性时间选择算法)

问题描述:给定n个整数,求其中第k小的数。

分析:显然,对所有的数据进行排序,即很容易找到第k小的数。但是排序的时间复杂度较高,很难达到线性时间,哈希排序可以实现,但是需要另外的辅助空间。

这里我提供了一种方法,可以在O(n)线性时间内解决Top k问题。关于时间复杂度的证明,不再解释,读者可以查阅相关资料。具体的算法描述如下:

算法:LinearSelect(S,k)

输入:数组S[1:n]和正整数k,其中1<=k<=n;

输出:S中第k小的元素

1. If  n<20  Then  将S中的元素排序后输出第k个元素,算法结束;

2.将S划分为无公共元素的 floor(n/5) 个分组,每组5个元素,第 i 个组记为Si;

3.用插入排序算法将每个组Si 排序,求得中位数 mi ,其中 i=1,2,3,...,floor(n/5);

4.递归调用本算法求得{mi | 1<=i floor(n/5)}的中位数(即第floor(n/10)小的元素)M;

5.划分S为A={x|x属于S 并且 x<M}, B={x|x属于S 并且 x=M}和C={x|x属于S 并且 x>M};

6. If |A|>k  Then  输出 LinearSelect(A,k);

7. ElseIf  |A|+|B|<k  Then  输出 LinearSelect(C,k-|A|-|B|);

8. Else   输出M,算法结束;

当然了,本题是求第k小,如果求第k大,可以转换成对应的第n-k小,同样可以求。这个算法以后会经常用到,一定要掌握,但是如果数据量不超过20个的话,也可以直接排序求。

完整的Java代码如下,代码写法都比较通用,读者可以很容易转换为其他语言实现:

 1 import java.lang.Math;
 2 import java.util.Arrays;
 3 import java.util.Scanner;
 4 public class Topk {
 5     public static int LinearSelect(int s[],int n,int k)
 6     {   int topk=0;
 7         if(n<=0)return topk;                          //如果数组为空,则返回0
 8         if(n<5)                                      //这里对应算法的第一步。这里我定义的是小于5个,则直接排序,读者也可以自己设定
 9         {
10                for(int i=0;i<n-1;i++)
11                    for(int j=i+1;j<n;j++)
12                        if(s[i]>s[j])
13                        {int t=s[i];s[i]=s[j];s[j]=t;}
14              topk= s[k-1];
15         }
16        else{
17
18         int ss[][]=new int[n/5][5];                  //对应算法的第二步
19         int j=-1;
20         for(int i=0;i<n/5*5;i++)
21         { if(i%5==0)j++;
22              ss[j][i%5]=s[i];
23
24         }
25
26         int sss[]=new int[n/5];
27         for(int i=0;i<n/5;i++)                      //对应算法的第三步
28         {
29             Arrays.sort(ss[i]);
30             sss[i]=ss[i][2];
31
32         }
33         Arrays.sort(sss);
34         int M=sss[n/5/2];                           //对应算法的第四步
35
36         int A[]=new int[n];                         //对应算法的第五步
37         int B[]=new int[n];
38         int C[]=new int[n];
39         int a=0,b=0,c=0;                      //作为三个结合的指针
40         for(int i=0;i<n;i++)                  //放入对应的集合中
41          {
42             if(s[i]<M)A[a++]=s[i];
43             if(s[i]==M)B[b++]=s[i];
44             if(s[i]>M)C[c++]=s[i];
45             }
46
47         if(a>k-1)topk=LinearSelect(A,a,k);             //对应算法第六步,我定义的数组是从下标0开始的忙,所以这里是k-1
48         else if(a+b<k-1)topk=LinearSelect(C,c,k-a-b);  //对应算法第七步
49         else topk=M;                                   //对应算法第八步
50
51         }
52         return topk;                                  //返回最终的Top k
53     }
54     public static void main(String[] args) {
55
56      int s[]={16,9,92,40,25,27};
57      int n=6;
58      int k=2;
59      System.out.print("数组为:");
60      for(int i=0;i<n;i++)
61      {
62          System.out.print(s[i]+",");
63      }
64      System.out.println();
65      System.out.println("第"+k+"小的数为:"+LinearSelect(s,n,k));
66
67     }
68
69 }

输出结果为:

数组为:16,9,92,40,25,27,
第2小的数为:16

时间: 2024-10-08 13:28:22

Top k问题(线性时间选择算法)的相关文章

线性时间选择算法

在一个由 n 个元素组成的集合中,第 i 个顺序统计量(order statistic)是该集合中第 i 小的元素.也就是说,最小值是第 1 个顺序统计量(i = 1),最大值是第 n 个顺序统计量(i = n). 中位数(median)是它所在集合的中点元素.当 n 为奇数时,中位数是唯一的,出现在 i = (n + 1)/2 处.当 n 为偶数时,存在两个中位数,下中位数 i = n/2 和上中位数 i = n/2 + 1 处.因此,不考虑 n 的奇偶性,中位数总是出现在 i = (n+1)

十大基础实用算法之寻找最小(最大)的k个数-线性查找算法

例如:输入1,2,3,4,5,6,7,8这8个数字,则最小的4个数字为1,2,3,4. 思路1:最容易想到的方法:先对这个序列从小到大排序,然后输出前面的最小的k个数即可.如果选择快速排序法来进行排序,则时间复杂度:O(n*logn) 注:针对不同问题我们应该给出不同的思路,如果在应用中这个问题的规模不大,或者求解前k个元素的频率很高,或者k是不固定的.那么我们花费较多的时间对问题排序,在以后是使用中可以线性时间找到问题的解,总体来说,那么思路一的解法是最优的. 思路2:在思路1的基础上更进一步

重拾算法之路——线性时间选择

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************** 第一章:分治与递归 线性时间选择 算法描述: 给定线性序集中n个元素和一个整数k,1 ≤ k ≤ n,要求找出这n个元素中第k小的元素.即如果将这n个元素依其线性序排列时,排在第k个位置的元素即为要找的元素.当k=1时,就是找最小元素,k=n时,就是找最大元素,

【分治法】线性时间选择(转)

转自:http://blog.csdn.net/liufeng_king/article/details/8480430 线性时间选择问题:给定线性序集中n个元素和一个整数k,1≤k≤n,要求找出这n个元素中第k小的元素,(这里给定的线性集是无序的). 1.随机划分线性选择 线性时间选择随机划分法可以模仿随机化快速排序算法设计.基本思想是对输入数组进行递归划分,与快速排序不同的是,它只对划分出的子数组之一进行递归处理. 程序清单如下: 1 //2d9-1 随机划分线性时间选择 2 #includ

Top k 问题

Top K的问题: 给出大量数据,找出其中前K个最大(小)的数,或者在海量数据中找到出现频率最好的前K个数. 一.给出大量数据(N个),找出其中前K个最大数(没有其他资源上的限制) 1.使用排序算法 直接使用排序算法,如快速排序,然后遍历找到最大的K个数.时间复杂度为O(NlogN): 2.部分排序 因为,只要求出前K个最大值,所以我们不必全部排好.思路是:随意选出K个数形成一个数组,然后按从大到小进行排序,再从剩下的数中,选取一个数和数组中的最小值进行比较,若小于最小值,则取下一个数继续比较:

Top K问题的两种解决思路

Top K问题在数据分析中非常普遍的一个问题(在面试中也经常被问到),比如: 从20亿个数字的文本中,找出最大的前100个. 解决Top K问题有两种思路, 最直观:小顶堆(大顶堆 -> 最小100个数): 较高效:Quick Select算法. LeetCode上有一个问题215. Kth Largest Element in an Array,类似于Top K问题. 1. 堆 小顶堆(min-heap)有个重要的性质--每个结点的值均不大于其左右孩子结点的值,则堆顶元素即为整个堆的最小值.J

经典算法之线性时间选择的C实现方法

线性时间选择问题,实际上是随机快速排序算法的一个引申,ie,通过对随机快速排序算法的小小改动就可以实现. 算法依然参考算法导论中的相关内容,需要注意的是, 代码中 的id 是指 所要 找的量在 现有区间 [low, high]中的位置, 因而,需要涉及 一定转化. ps: mid - low + 1 表示 我们划分出来的第一个区间的长度大小. 源代码如下: // =====================[随机选择 ]================== // @ author : zhyh20

[图解算法] 线性时间选择——//递归与分治策略//【图解才是最直观】

1 #include <ctime> 2 #include <iostream> 3 using namespace std; 4 5 template <class Type> 6 void Swap(Type &x,Type &y); 7 8 inline int Random(int x, int y); 9 10 template <class Type> 11 void BubbleSort(Type a[],int p,int r

排序算法Java版,以及各自的复杂度,以及由堆排序产生的top K问题

常用的排序算法包括: 冒泡排序:每次在无序队列里将相邻两个数依次进行比较,将小数调换到前面, 逐次比较,直至将最大的数移到最后.最将剩下的N-1个数继续比较,将次大数移至倒数第二.依此规律,直至比较结束.时间复杂度:O(n^2) 选择排序:每次在无序队列中"选择"出最大值,放到有序队列的最后,并从无序队列中去除该值(具体实现略有区别).时间复杂度:O(n^2) 直接插入排序:始终定义第一个元素为有序的,将元素逐个插入到有序排列之中,其特点是要不断的 移动数据,空出一个适当的位置,把待插