問題描述:有 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]