求 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?

你可能感兴趣的:(我的思考,算法)