详解 TopK
01 TopK 问题
- TopK 问题是在实际业务中经常出现的典型问题,例如微博的热门排行就属于 TopK 问题。
- TopK 一般是要求在 N 个数的集合中找到最小或者最大的 K 个值,通常 N 都非常得大。TopK 可以通过排序的方式解决,但是时间复杂度较高,一般是 O(nk),这里我们来看看更加高效的方法。
- 如下图所示,首先取前 K 个元素建立一个大根堆,然后对剩下的 N-K 个元素进行遍历,如果小于堆顶的元素,则替换掉堆顶元素,然后调整堆。当全部遍历完成时,堆中的 K 个元素就是最小的 K 个值。
- 这个算法的时间复杂度是
N*logK
。 - 算法的优点是不用在内存中读入全部的元素,能够适用于非常大的数据集。
02 TopK 变种问题
- TopK 变种的问题,就是从 N 个有序队列中,找到最小或者最大的 K 个值。这个问题的不同点在于,是对多个数据集进行排序。由于初始的数据集是有序的,因此不需要遍历完 N 个队列中所有的元素。因此,解题思路是如何减少要遍历的元素。
解题思路如下图所示。
- 第一步先用 N 个队列的队头元素,也就是每个队列的最小元素,组成一个有 K 个元素的小根堆。方式同 TopK 中的方法。
- 第二步获取堆顶值,也就是所有队列中最小的一个元素。
- 第三步用这个堆顶元素所在队列的下一个值放入堆顶,然后调整堆。
- 最后重复这个步骤直到获取够 K 个数。
这里还可以有个小优化就是第三步往堆顶放入新值时,跟堆的最大值进行一下比较,如果已经大于堆中最大值,就可以提前终止循环了。这个算法的时间复杂度是 (N+K-1)*logK
,注意这里与队列的长度无关。