Top-K Solution

aaakz0347 发布于 2 天前 17 次阅读


1. 小规模 / 一次性批量数据

  • 排序 + 截取
    • 把所有数据整体排序(复杂度 O(N log N)),然后取前 K 个。
    • 适合一次性查询、不需要频繁更新的情况。

2. 大规模数据 / K 远小于 N

  • 堆(Heap)
    • 维护一个大小为 K 的最小堆(找最大 K)或最大堆(找最小 K)。
    • 插入每个元素时:
      • 若堆未满 → 直接加入;
      • 若堆满且新元素更优 → 替换堆顶。
    • 复杂度 O(N log K),比全量排序高效。
    • 常用于实时排行榜。

为什么是小顶堆?

因为我们希望淘汰那些相对较小的元素。根据小顶堆的定义,堆顶始终是当前 K 个元素中的最小值。当我们遇到一个比这个最小值更大的新元素时,它就有资格进入前 K 名。这时,我们可以用新元素替换掉当前最小的元素,并重新调整堆。这样,堆中始终保持着最大的 K 个元素。

如果我们要找最小的 K 个元素,则使用同样的方法,但构建一个大顶堆


除了堆,还有一些其他方法可以解决 Top-K 问题,但它们通常在特定场景下使用,或者效率不如堆。

  • 快速选择(Quickselect)
    • 这是一种基于快速排序(Quicksort)的分治算法,但只处理一边的分区。
    • 它的平均时间复杂度是 O(N),比堆的 O(N log K) 更好,但在最坏情况下可能退化到 O(N^2)。
    • 它通常用于离线(所有数据一次性可用)且不需要持续维护的场景。
    • 实现起来比堆复杂,且不如堆稳定。
  • 计数排序或桶排序(Counting Sort / Bucket Sort)
    • 如果数据的范围有限且相对较小,可以使用这些线性时间复杂度的排序算法。
    • 它们的时间复杂度是 O(N + M),其中 M 是数据范围。
    • 这种方法有很强的局限性,不适用于数据范围非常大或数据类型为浮点数的情况。

3. 分布式/大数据场景

  • MapReduce / 分布式 Top-K (分治的思想)
    • 每个分片先算出局部 Top-K;
    • 汇总后再取全局 Top-K。
    • 大幅减少通信和内存压力。

4.拓展延伸

1. 从十亿数据中找到出现次数最多的十个数字

用hash树记录每个数字出现的频率,转化为在各个数字的频率中找到Top K的问题。

2. 从十亿数据中找到升序的第七亿个数

2.1 快速排序

  • step1:以数组最后一个元素作为基准值,将数据partition为[left,mid-1][mid,right]两个区间,其中[left,mid-1]的数都小于base值,[mid,right]都大于等于base值
  • step2:计算[mid, right]数组长度L:
    • 如果L等于三亿,那么[left,mid-1]中的七亿个数字小于[mid,right]中的三亿个数字,只需要在[left,mid-1]中找到最大值即可
    • 如果L大于三亿,则将问题转化为在[mid, right]中找到第(L-三亿)个数
    • 如果L小于三亿则问题还是在[left,mid-1]中找到第七亿大的数

在step2中,如果数据量级足够小则可以直接进行排序得到答案。

2.2 桶排序

假如数据的范围是有限的(有最大值和最小值)且较均匀分布,那么使用桶排序可以进一步提升效率:

  • step1:获取数据的max和min
  • step2:将所有的n份数据分到m个文件中,同时记录每份子文件中的数据量
  • step3:从小到大累加文件数据量,找到第七亿个数所在的文件,记录前面文件的总数据量count
  • step4:将问题转化为在第七亿个数所在的文件中寻找第(七亿-count)个数

在step4中,如果内存足够存放所有数据,可以直接排序获取第七亿个数字。

3. 对十亿数据进行排序:多路归并排序

此作者没有提供个人介绍。
最后更新于 2025-09-15