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個元素
考慮兩種情況
- k剛好爲位於pivot的左邊,那麼只需要繼續排序[left:k]
- 當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/