直接法--堆排序
目录
讲解
堆排序完成后,直接切片前n名,也可以完成取topn,之前讲解过堆排序,想要学习的移步堆排序
与之不同的是,需要建立小根堆,在排序就是逆序了
代码演示
下面展示这种方法的代码:
# 小根堆
def sift(li, low, high):
i = low
tmp = li[i]
j = 2 * i + 1
# 之所以用while是因为都要比,找不到不行
while j <= high:
if j + 1 <= high and li[j + 1] < li[j]:
j = j + 1
if tmp > li[j]:
li[i] = li[j]
i = j
j = 2 * i + 1
else:
break
li[i] = tmp
def top_k(li,k):
n = len(li)
# 从最后一个叶子结点的父节点开始向下调整
for i in range((n - 2) // 2, -1, -1):
sift(li, i, n - 1)
# 把数从根节点拿出来排序,从n-1开始,排好一个,往前一位
for i in range(n - 1, -1, -1):
li[i], li[0] = li[0], li[i]
sift(li, 0, i - 1)
return li[k]
以上代码和堆排序几乎一模一样,只添加了一个形参k并且添加了返回值li[k]
时间复杂度
这样的时间复杂度是O(nlogn)
其实还有更好用的算法来求topk
巧妙运用--堆排序
讲解
先建立一个个数为k(k<n),的小根堆,再从后面的n-k个元素和堆顶的数比较,比它大则进入堆里,然后向下调整,就这样进行n - k次就可以得到一个k个最大数的小根堆,然后在利用堆排序的方法,把堆元素排好序,就得到topk了
代码演示
由于同样是用上面的sift的小根堆调整函数,就不再重复写了,直接写出topk的过程.
def topk(li, k):
lik = li[0:k] # 用新列表接收小根堆
n = len(li)
for i in range((k - 2) // 2, -1, -1):
sift(lik, 0, k - 1)
for i in range(k, n):
if lik[0] < li[i]:
lik[0] = li[i]
sift(lik, 0, k - 1)
for i in range(k - 1, -1, -1):
lik[i], lik[0] = lik[0], lik[i]
sift(lik, 0, i - 1)
return lik
时间复杂度
这样的时间复杂度为O(nlogk),缩短了时间,但要记得堆排序的不稳定性,在在没有重复值的数组中排序前topk,堆排序或许是不错的选择.
586

被折叠的 条评论
为什么被折叠?



