topk問題解決思路

topk問題解決方法

顧名思義,topk問題就是求解最大或者最小k個數字的一類問題

常見的解決思路是先排序,然後取依次取k個,最大堆/最小堆,

排序法

思路:先進行排序然後依次取k個,本次講都是取topk大

def q_sort(ary, left, right):
    if left<=right:
    
        ary[right],ary[left] = ary[left],ary[right]
        store = left
        for i in range(left,right):
            if ary[i] >= ary[right]:
                ary[store],ary[i] = ary[i],ary[store]
                store+=1
        ary[store],ary[right] = ary[right],ary[store]
        q_sort(ary,left,store-1)
        q_sort(ary,store+1,right)

def topk_sort(ary, k):
    q_sort(ary,0,len(ary)-1)
    return ary[:k]

複雜度: 快排是nlogn, 取k個, 所以最終的時間複雜度是O(nlogn)

最小堆

tips:求最大用最小堆,求最小用最大堆
思路,先使用前k個元素,建立一個最小堆,然後不斷與堆頂元素進行比較

def build_heap(ary):
    #建立堆
    for i in range(len(ary)//2,-1,-1):
        adjust_heap(ary,i,len(ary))

def adjust_heap(ary, i, size):
    #調整堆
    left = 2*i + 1
    right = 2*i + 2
    min_index = i
    if left<size and ary[left]<ary[min_index]:
        min_index = left 
    if right<size and ary[right]<ary[min_index]:
        min_index = right 

    if min_index!=i:
        ary[min_index],ary[i] = ary[i],ary[min_index]
        adjust_heap(ary,min_index,size)

def topk_heap(ary,k):
    if len(ary)<k:
        return []
    
    heap = ary[:k]
    build_heap(heap)
    for i in range(k,len(ary)):
        if ary[i]>heap[0]:
            heap[0] = ary[i]
            adjust_heap(heap,0,len(heap))
    return heap

由於堆本地上是一顆二叉搜索樹,因此每次調整時間複雜度都是O(logk) 注意是logk。 因爲堆大小隻有k,同時會進行n次調整,因此算法時間複雜度爲O(nlogk)

改進快排(又稱隨機選擇)

考慮到求解的是topk問題,有一個直觀的思想就是隻排序前k個,是不是就可以解決topk的問題啦

按着這種想法,我們改造快速排序,使其只排前k個元素

考慮兩種情況

  1. k剛好爲位於pivot的左邊,那麼只需要繼續排序[left:k]
  2. 當k位於pivot的右邊時,我們需要繼續排序ary[pivot+1,right]
def quick_select(ary,left,right, k):
    if left<=right:
        ary[left],ary[right] = ary[right],ary[left]
        store = left 
        for i in range(left,right):
            if ary[i]>=ary[right]:
                ary[store],ary[i] =ary[i],ary[store]
                store+=1
        ary[store],ary[right] = ary[right],ary[store]
        if k<=store:
            quick_select(ary,left,store-1,k)
        else:
            quick_select(ary,store+1,right,k)

def topk_quick(ary,k):
    quick_select(ary,0,len(ary)-1,k)
    return ary[:k]

考慮時間複雜度, quick_select本質上一個二分法,因此複雜度時logn, 被會調用k次(之際應該小於k),簡單標記爲O(klogn).

練習

https://leetcode-cn.com/problems/top-k-frequent-elements/submissions/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章