从一堆无序的数中(共n个数)找到最小的K个数,这也算是一道比较经典的题目了,关于这道题目的解法,一般有几种:
方法1:先对所有的数据进行排序,然后直接找出前K个数来,即最小的K个数。时间复杂度为O(N*logN)。
方法2:采用类似快排的思想,只要找到第K小的数值的位置的话,那么数组中的前K个数值一定是最小的K个数,但是这K个数不一定是排好序的,关于找到第K个小的数值的方法卡参考我之前的文章:http://www.cnblogs.com/wangkundentisy/p/8810077.html。
当然,也可以参考《剑指offer(第二版)》面试题40。这种方法的期望时间复杂度为O(N),但是适用于大多数情况;最坏情况下时间复杂度可达到O(N^2)。
方法3:利用一个大顶堆,具体过程如下:
选取数据中前K个数(或者任意K个数)构成一个大顶堆,这个堆的根节点就是这K个数中的最大值,然后从剩余的n-K个数中依次找一个数与根节点的数比较,如果比根节点的数大的话,则跳过;如果比根节点的数小的话,就把根节点删除,并把这个数值加入到这个堆中,然后再把这个堆调整成大顶推,重复上述过程,直到比较完剩余的n-k个数。
这种方法的时间复杂度为O(N*logK)。
方法4:利用堆排序的思想,建立一个大小为n的小顶堆,由于小顶堆的顶点一定是n个数中的最小值,所以每次删除根节点,然后在调整堆,重复K次,就能找到最小的K个值了。(与堆排序的过程一致)这种算法的时间复杂度为O(K*logN)。
==================================================================================================分割线=================================================
1.当n的值不是很大时,以上几种方法的性能相差并不是很大,通常方法2用的比较多。
2.那么当n很大的时候,方法1和方法2就不适用了。通常采用方法3。(关于海量数据处理的问题可参考july的博客:https://blog.csdn.net/v_july_v/article/details/7382693)那么,为什么不能采用方法4呢?以下是个人的一些见解:
在n非常大的时候,数据需要存到硬盘上,而K相对却很小,采用方法3的话,可以在内存上轻易维护大小为K的堆的情况下,在减少磁盘I/O上会有一定的优势,因为每个元素只需要被读取一次。即方法3只需将大小为K的堆写入内存,而方法4需要将所有的n个数据写入内存,相比而言方法3对内存要求更小,更具有优势。所以,在有限的资源下,海量数据处理问题,通常采用方法3.