题目:输入n个整数,找出其中最小的k个数。
例如输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
1、最简单的思路:对输入的n个整数排序,输出位于前面的K个数。
第一想到的是快速排序,时间O(nlgn).然后输出前面的k个数,O(k)
所以时间复杂度为O(nlogn+k)=O(nlgn).
2、遍历n个数,先把最先遍历到得k个数存入大小为k的数组之中,对这k个数,利用选择或交换排序,找到k个数中的最大数kmax,用时O(k),后再继续遍历后n-k个数,x与kmax比较:
如果x小于kmax,则x代替kmax,并再次重新找出k个元素的数组中最大元素kmax;如果x>kmax,则不更新数组。这样,每次更新或不更新数组的所用的时间为O(k)或O(0),整趟下来,总的时间复杂度平均下来为:n*O(k)=O(n*k)。
3、更好的办法是维护k个元素的最大堆,原理与上述第2个方案一致,即用容量为k的最大堆存储最先遍历到的k个数,并假设它们即是最小的k个数,建堆费时O(k)后,有kmax设为大顶堆中最大元素。继续遍历数列,每次遍历一个元素x,与堆顶元素比较,x小于kmax,更新堆(用时logk),否则不更新堆。这样下来,总费时O(k+(n-k)*logk)=O(n*logk)。此方法得益于在堆中,查找等各项操作时间复杂度均为logk(不然,就如上述思路2所述:直接用数组也可以找出前k个小的元素,用时O(n*k))。
4、N个数存储在数组S中,从数组中随机选取一个数X,把数组划分为Sa和Sb俩部分,Sa<=X<=Sb,如果要查找的k个元素小于Sa的元素个数,则返回Sa中较小的k个元素,否则返回Sa中所有元素+Sb中小的k-|Sa|个元素。
像上述过程一样,这个运用类似快速排序的partition的快速选择SELECT算法寻找最小的k个元素,在最坏情况下亦能做到O(N)的复杂度。不过值得一提的是,这个快速选择SELECT算法是选取数组中“中位数的中位数”作为枢纽元,而非随机选取枢纽元。