算法题之找出数组里第K大的数
问题:找出一个数组里面前K个最大数。
解法一(直接解法):
对数组用快速排序,然后直接挑出第k大的数。这种方法的时间复杂度是O(Nlog(N))。N为原数组长度。
这个解法含有很多冗余,因为把整个数组都排序了,而实际上我们不需要这样做。
解法二(K数组排序):
首先,创建一个长度为K的空数组。从原数组中先挑出K个数进行排序并放到这个空数组中。这一步的时间复杂度是O(Klog(K))。
接着,从剩下的N-K个值中,依次遍历并与上面数组的末尾的数(即里面的最大数)相比较,并插入到合适位置,需要执行(N-K)步。
总平均时间复杂度是O(Klog(K)×(N-K))。
另外,如果采用最大堆或红黑树的方法来调整插入删除K数组,时间复杂度是O(log(K)),总时间复杂度是O(Nlog(K))。
解法三(递归法):
当K值非常大或非常接近N的时候,那么上面的两种解法的时间复杂度都会显著增大,这就需要有更好的方法来应对:直接划定中值区间,通过比较的方法,找到第K个数所在的数值区间,然后二分搜索递归下去。
假设N=100万,K=50万的情况。
先选取一个中值元素(该中值是否合理将影响到算法效率,其原因同快速排序),然后将大于等于该数的元素放到其右侧,小于该数的放到左侧。如7 4 6 8 0 -1,选取6作为中值元素,则结果应该为4 0 -1 6 8 7,接下来比较K值和现在的中值元素6所在索引(3)。
如果K小于索引3,则处于包括中值元素在内的右边的元素即是前K个最大数中的前(3(索引) - K + 1)个最大数,予以保存,同时需在索引0 ~ 2间再进行递归操作继续选取第K名。
如果K大于索引3,则在4 ~ 5中递归选取第K - 3(索引) - 1名即可。
还有一关键是可以为递归中的数组长度选取一临界点,小于该临界则进行全排序,而不再进行递归操作。
平均时间复杂度是O(N)。
当问题规模不是很大时,比如数组大小N很小,N为100数量级,可以不用太追求算法的高效性,因为对于问题规模不大时,上面三种算法的运行时间相差并不大,
完全可以考虑采用第一种或者第二种比较简单的实现方式。