堆排序扩展--topk问题

直接法--堆排序 

目录

直接法--堆排序 

讲解

代码演示

时间复杂度

巧妙运用--堆排序

讲解

代码演示

时间复杂度


讲解

堆排序完成后,直接切片前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,堆排序或许是不错的选择.

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值