解法一:
假设元素的数量不大,用快排或堆排序都是不错选择,平均时间复杂度都是O(N*log N)。然后取出前K个,O(K)。总时间复杂度O(N*log N)+ O(K)=O(N* log N)。
当K=1,上述算法也是O(N* log N)的复杂度,而显然我们可以通过N-1次的比较和交换得到结果。上述算法对整个数组都进行了排序,而原题目只要求最大的K个数,并不需要前K个数有序,也不需要前K个数有序,也不需后N-K个数有序。
如何避免后N-K个数的排序呢?我们需要部分排序算法,选择排序和交换排序都可以。把N个数中的前K个数排序出来,复杂度是O(N*K).
上述哪一种更好?O(N* log N) 还是 O(N*K)? 这取决于K的大小,需要从面试官那里弄清楚。在K<= log N时,选择部分排序。
解法二:
设N个数存储在数组S中,从数组S中随机找出一个元素X,把数组分为两部分Sa和Sb。Sa中所有元素都大于等于X,Sb中所有元素小于X。
1)Sa中元素的个数小于K,Sa中所有的数和Sb中最大的K-|Sa|个元素 (|Sa|指Sa中元素的个数)就是数组S中最大的K个数。
2)Sa中元素的个数大于或等于K,则需要返回Sa中最大的K个元素。
这样递归下去,不断把问题分解成更小问题,平均时间复杂度O(N*log k).
解法三:
寻找N个数中最大的K个数,本质上就是寻找最大的K个数中最小的那个,那就是第K大的数。可以使用二分搜索的策略。对于一个给定的数p,可以在O(N)的时间复杂度内找出所有不小于p的数。假如N个数中最大的数是Vmax,最小的数位Vmin,那么这N个数中的第K大数一定在区间[Vmin,Vmax]之间。那么可以,在这个区间内二分搜索N个数中的第K大数p。
while(Vmax - Vmin > delta) {
Vmid = Vmin +(Vmax - Vmin) * 0.5;
if(f(arr, N, Vmid) >= k)
Vmin=Vmid;
else
Vmax=Vmid;
}