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/