從數組中找到topK的元素(序號)


問題:
在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()。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM