求 top k有哪些方法

大家好, 此文章分享求top k有哪些方法

求 topk, 理解起来不难,就是很多元素中,找出前K个最大或者最小

假设我们求最大K元素

思路有以下几种:

1. 全局排序

所有元素加载到内存,来个全局排序,取前K个元素即可

最优算法复杂度: O(n * log n)

这个是最容易想到的

在没有时间和空间的复杂度要求,这样是没有问题。

2. 局部排序

我们只需要取前K个元素, 动用全局排序,显然是没有必要的

我们可以只排序TopK个元素,每拍一次序能确定前K个元素的一个,很自然会想到冒泡排序

采用冒泡排序K次就能得到TopK个元素了

算法复杂度: O( n * K)

3. 堆排序

局部排序中:采用冒泡每次排序的复杂度在 O(n), 不是局部排序的最优算法

局部排序得到:K个元素, 可以采用堆排序

首先

构建只有K个元素的最小堆或最大堆

  •  最小堆: 求topk 最大K元素
  • 最大堆: 求topk 最小K个元素

构建K个元素的堆,其算法复杂度: O(k log K)

其次,

遍历剩余元素 ( n - k)个, 分别与堆顶元素比较,此处构建的是最小堆

  1.   元素 > 堆顶元素: 替换堆顶元素, 调整堆  O(log k)
  2.   元素 < 堆顶元素, 该元素不可能是最后的K个元素,直接跳过
  3. 元素 == 堆顶元素, 该元素已存在堆中, 直接跳过

当遍历完剩余的(n - k) 个元素后, 堆中的K个元素就是topk

剩余元素遍历算法复杂度,最坏:O((n-k) * log k)

最终算法复杂度: O(n * log K), 优于冒牌排序的 O( n * K)

这个 O(n * log K) 一般就是大多数人能想到的最优方案了

其实还有更优的方案, 让我们来看最后一种方法

4.  随机选择

该方法的逻辑,受快速排序中得到partition的过程所启发

那么什么是partition?我简单介绍一下

partition操作: i = partition(array, 1, n) (  1 <= i <= n )

每次partition操作得到的 i 满足以下条件:

  •  a[i]  左边的元素比 a[i] 大或者小
  •  a[i]  右边的元素比 a[i] 小或者大

此处我们每次partition之后: 左边元素比a[i]大, 右边比a[i]小

  • 如果 i > k,  那么a[i]左边的元素都大于k,于是只递归a[1, i-1]里第k大的元素即可
  • 如果 i < k, 说明第k大的元素在a[i]的右边,于是只递归a[i+1, n]里第 k - i大的元素即可
  • 如果 i == K,第k大的元素就是当前i位置的, 数组前k个元素就是结果

该方法每次partition的复杂度,最大是: O( n)

每次partition都是分开独立的, 所有整个算法的时间复杂度才: O( n) 

该算法是优于前面提到的所有算法的

扩展

如果数据量特别大, 我们如果计算topk?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JYCJ_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值