排序中topK那点事(转)

问题描述:有 N (N>1000000)个数,求出其中的前K个最小的数(又被称作topK问题)。

 

这类问题似乎是备受面试官的青睐,相信面试过互联网公司的同学都会遇到这来问题。下面由浅入深,分析一下这类问题。

 

思路1:最基本的思路,将N个数进行完全排序,从中选出排在前K的元素即为所求。有了这个思路,我们可以选择相应的排序算法进行处理,目前来看快速排序,堆排序和归并排序都能达到O(NlogN)的时间复杂度。当然,这样的答案也是无缘offer的。

 

思路2:可以采用数据池的思想,选择其中前K个数作为数据池,后面的N-K个数与这K个数进行比较,若小于其中的任何一个数,则进行替换。这种思路的算法复杂度是O(N*K),当答出这种算法时,似乎离offer很近了。

 

有没有算法复杂度更低的方法呢?

 

从思路2可以想到,剩余的N-K个数与前面K个数比较的时候,是顺序比较的,算法复杂度是K。怎么在这方面做文章呢? 采用的数据结构是堆。

 

思路3:大根堆维护一个大小为K的数组,目前该大根堆中的元素是排名前K的数,其中根是最大的数。此后,每次从原数组中取一个元素与根进行比较,如小于根的元素,则将根元素替换并进行堆调整(下沉),即保证大根堆中的元素仍然是排名前K的数,且根元素仍然最大;否则不予处理,取下一个数组元素继续该过程。该算法的时间复杂度是O(N*logK),一般来说企业中都采用该策略处理topK问题,因为该算法不需要一次将原数组中的内容全部加载到内存中,而这正是海量数据处理必然会面临的一个关卡。如果能写出代码,offer基本搞定。

 

还有没有更简单的算法呢?答案是肯定的。

 

思路4:利用快速排序的分划函数找到分划位置K,则其前面的内容即为所求。该算法是一种非常有效的处理方式,时间复杂度是O(N)(证明可以参考算法导论书籍)。对于能一次加载到内存中的数组,该策略非常优秀。如果能完整写出代码,那么相信面试官会对你刮目相看的。

 

下面,给出思路4的Python代码:

 

def partition(data,start,end):
    if len(data)==1:
        return 0
    small=start
    p=small+1
    while p<=end:
        if data[p]<data[start]:
            small+=1
            data[p],data[small]=data[small],data[p]
        p+=1
    data[start],data[small]=data[small],data[start]
    return small

def topk(data,k):
    if k==0:
        return None
    if len(data)<=k:
        return data
    left=0
    right=len(data)-1
    s=partition(data,left,right)
    while s+1!=k: #下标加1才能与前k的k进行比较
        if s+1>k:
            right=s-1
        if s+1<k:
            left=s+1
        s=partition(data,left,right)
    
    return data[:k]
 
posted @ 2017-06-14 10:40  光彩照人  阅读(8756)  评论(2编辑  收藏  举报