编程之美--求最大的k个数

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

2.     1  至于选取什么的排序方法,我想你可能会第一时间想到快速排序,我们知道,快速排序平均所费时间为n*logn,然后再遍历序列中前k个元素输出,即可,总的时间复杂度为On*logn+k=On*logn。。

3.     3  当然,更好的办法是维护k个元素的最大堆,原理与上述第2个方案一致,即用容量为k的最大堆存储最先遍历到的k个数,并假设它们即是最小的k个数,建堆费时Ok)后,有k1<k2<...<kmaxkmax设为大顶堆中最大元素)。继续遍历数列,每次遍历一个元素x,与堆顶元素比较,x<kmax,更新堆(用时logk),否则不更新堆。这样下来,总费时Ok+n-k*logk=On*logk。此方法得益于在堆中,查找等各项操作时间复杂度均为logk(不然,就如上述思路2所述:直接用数组也可以找出前k个小的元素,用时On*k))。

4.     4 按编程之美第141页上解法二的所述,类似快速排序的划分方法,N个数存储在数组S中,再从数组中随机选取一个数X随机选取枢纽元,可做到线性期望时间ON)的复杂度),把数组划分为SaSb俩部分,Sa<=X<=Sb,如果要查找的k个元素小于Sa的元素个数,则返回Sa中较小的k个元素,否则返回Sak个小的元素+Sb中小的k-|Sa|个元素。像上述过程一样,这个运用类似快速排序的partition的快速选择SELECT算法寻找最小的k个元素,在最坏情况下亦能做到ON)的复杂度。不过值得一提的是,这个快速选择SELECT算法是选取数组中中位数的中位数作为枢纽元,而非随机选取枢纽元。

编程之美的答案:

5.  Kbig(S, k):  

6.       if(k <= 0):  

7.            return []     // 返回空数组  

8.       if(length S <= k):  

9.            return S  

10.       (Sa, Sb) = Partition(S)  

11.       return Kbig(Sa, k).Append(Kbig(Sb, k – length Sa)  

12.    

13.  Partition(S):  

14.       Sa = []            // 初始化为空数组  

15.       Sb = []        // 初始化为空数组  

16.       Swap(s[1], S[Random()%length S])   // 随机选择一个数作为分组标准,以  

17.                          // 避免特殊数据下的算法退化,也可  

18.                          // 以通过对整个数据进行洗牌预处理  

19.                          // 实现这个目的  

20.       p = S[1]  

21.       for i in [2: length S]:  

22.           S[i] > p ? Sa.Append(S[i]) : Sb.Append(S[i])  

23.                              // p加入较小的组,可以避免分组失败,也使分组  

24.                              // 更均匀,提高效率   

25.  length Sa < length Sb ? Sa.Append(p) : Sb.Append(p)  

26.  return (Sa, Sb)  

你可能感兴趣的:(编程之美--求最大的k个数)