問題:
在n個數中找出最大的k個數。
多次求min()或求max()
最簡單的方法是對大小為k的數組進行n次求min計算(或者對大小為n的數組進行k次求max計算)最后能夠找出最大k個數。復雜度是O(nk)。
代碼:
def topK_mink(num_list,k):
topK=num_list[:k]
for i in range(k,len(num_list)):
topK_min=min(topK)
if num_list[i]>topK_min:
topK[topK.index(topK_min)]=num_list[i]
return topK
使用小根堆
維護一個大小為k的小根堆,從頭到尾掃描n個數,如果當前數比堆頂大,替換堆頂,這樣掃描到最后堆中保存的是最大的k個數。復雜度是O(nlogk)
代碼:
import heapq
def topK_heapq(num_list,k):
array = []
for i in range(len(num_list)):
if len(array) < k:
heapq.heappush(array, num_list[i])
else:
array_min = array[0]
if num_list[i] > array_min:
heapq.heapreplace(array, num_list[i])
topK=array
return topK
使用大根堆
維護一個大小為n的大根堆,每次彈出堆頂元素,共彈出k次。復雜度O(klogn)
代碼:略
快速選擇BFPRT
借用快速排序中思想,在快排中每次用一個軸將數組划分為左右兩部分,軸左邊的數都小於軸,軸右邊的數都大於軸,軸所在的位置和排好序后的位置相同。這里只要找到第k大的數作為軸進行划分,那么就找到了最大的k個數。期望復雜度是:O(n)
代碼:
def topK_partition(arr,k):
def partition(num_list,left,right,k):
flag=num_list[left]
i=left
j=right
while i<j:
#print(flag,i,j,num_list)
if num_list[i]>flag:
i+=1
elif num_list[j]<flag:
j-=1
else:
if num_list[i]==num_list[j]:
j-=1
num_list[i],num_list[j]=num_list[j],num_list[i]
#print(flag,num_list)
if i<k:
return partition(num_list,i+1,right,k)
if i>k:
return partition(num_list,left,i-1,k)
return num_list[:k]
return partition(arr[:],0,len(random_list)-1,k)
測試代碼:
import numpy as np
import time
def judge(ans,k1):
for i in ans:
if i-k1<0:
return False
return True
k=1000
n=25000
random_list=[np.random.randint(n*0.5) for i in range(n)]
real_ans=sorted(random_list,reverse=True)[:k+1]
k1=real_ans[-1]
t1=time.time()
ans=topK_heapq(random_list,k)
t2=time.time()
print(judge(ans,k1),t2-t1)
t1=time.time()
ans=topK_partition(random_list,k)
t2=time.time()
print(judge(ans,k1),t2-t1)
t1=time.time()
ans=topK_mink(random_list,k)
t2=time.time()
print(judge(ans,k1),t2-t1)
結果:
topK_mink()沒有任何優勢
topK_partition()的運行時間不穩定
topK_heapq()運行時間穩定
雖然期望復雜度topK_partition()優於topK_heapq(),但是topK_partition()計算開銷比topK_heapq()多。
當n小時,用topK_heapq()比topK_partition()好
當n大,k小時,topK_heapq()用時也較短。
當n大,k大時(n>10,000,000),用topK_partition()。
