寻找最大的K个数(TOP K算法)

前言:

本文是对编程之美第2.5节以及博文http://blog.csdn.net/v_july_v/article/details/6370650的一些总结和心得

问题描述:

有很多个无序的数,怎么从中选出其中最大的若干数呢?

这个问题中的很多可以是几个数也可以使成百上亿的数,针对此问题我们有以下解法:

解法一:

咱们先简单的理解,要求一个序列中最小的k个数,按照惯有的思维方式,很简单,先对这个序列从小到大排序,然后输出前面的最小的k个数即可。

至于选取什么的排序方法,我想你可能会第一时间想到快速排序,我们知道,快速排序平均所费时间为n*logn,然后再遍历序列中前k个元素输出,即可,总的时间复杂度为O(n*logn+k)=O(n*logn)。但这只能针对数据量不大的情况下可以实现。

解法二:

类似快速排序的划分方法,N个数存储在数组S中,再从数组中随机选取一个数X(随机选取枢纽元,可做到线性期望时间O(N)的复杂度),把数组划分为Sa和Sb俩部分,Sa<=X<=Sb,

1,如果要查找的k个元素小于Sa的元素个数,则返回Sa中较小的k个元素;

2,否则返回Sa中k个小的元素+Sb中小的k-|Sa|个元素。

这样递归下去,不断把问题分解成更小的问题,平均时间复杂度O(Nlog2K)。

解法三:

RANDOMIZED-SELECT,每次都是随机选取数列中的一个元素作为主元,在0(n)的时间内找到第k小的元素,然后遍历输出前面的k个小的元素。 如果能的话,那么总的时间复杂度为线性期望时间:O(n+k)=O(n)(当k比较小时)。如果不做随机选取,这里的最坏情况和快速排序一样可能达到O(n2)。

解法四:

我们已经得到了三个解法,不过这三个解法有个共同的地方,就是需要对数据访问多次,那么就有下一个问题,如果N很大呢,100亿?

当然,更好的办法是维护k个元素的最小堆,原理与上述第2个方案一致,即用容量为k的最小堆存储最先遍历到的k个数,并假设它们即是最大的k个数,建堆费时O(k)后,有k1<k2<...<kmax(kmax设为大顶堆中最小元素)。继续遍历数列,每次遍历一个元素x,与堆顶元素比较,x>kmax,更新堆(用时logk),否则不更新堆。这样下来,总费时O(k+(n-k)*logk)=O(n*logk)

解法五:

计数排序,时间复杂度虽能达到O(n),但限制条件太多,我们必须假设所有N个整数都是正整数,且他们的取值范围不大,才可以考虑申请空间,记录每个整数出现的次数,然后再从大到小取最大的K个数。

综合考虑我们一般选取上面的解法四来做我们的算法,也就是我们常常说的TOP K算法。

 

 

你可能感兴趣的:(编程,算法,存储,n2)