从M个元素中查找最小的N个元素时,使用大顶堆的效率比使用小顶堆更高,为什么?
我们有一个长度为 M 的数组,现在我们想从中找出 最小的 N 个元素。例如:
int a[10] = {12, 3, 5, 7, 19, 0, 8, 2, 4, 10};
从中找出 最小的 4 个元素。
正确方法:使用大小为 N 的「大顶堆」
原因分析:
我们想保留最小的 4 个元素,因此可以使用一个大顶堆,堆的作用是“维护最小的 N 个数”。
思路如下:
- 初始化:先把前 4 个数放入堆中 →
12, 3, 5, 7
。- 堆顶为最大值 12,表示目前“最小的 4 个元素”中最大的那个是 12。
- 从第 5 个数开始往后遍历数组,只要当前数 < 堆顶,就替换堆顶。
- 最终堆中留下的是“最小的 4 个数”。
具体步骤(维护一个大顶堆):
初始数组:
{12, 3, 5, 7, 19, 0, 8, 2, 4, 10}
步骤:
- 初始化堆(大顶堆):
[12, 3, 5, 7]
→ 堆顶是 12 - 接下来遍历:
当前元素 | 与堆顶比较 | 操作 | 新堆内容(无序表示) |
---|---|---|---|
19 | > 12 | 略过 | [12, 3, 5, 7] |
0 | < 12 | 删除 12,插入 0 | [7, 3, 5, 0] |
8 | > 7 | 略过 | [7, 3, 5, 0] |
2 | < 7 | 删除 7,插入 2 | [5, 3, 2, 0] |
4 | < 5 | 删除 5,插入 4 | [4, 3, 2, 0] |
10 | > 4 | 略过 | [4, 3, 2, 0] |
最终堆中元素:[0, 2, 3, 4]
✅
如果用小顶堆会怎么样?
假如你误用了小顶堆,初始堆是 [3, 12, 5, 7]
,堆顶是 3。
你遍历后面的元素时,堆顶一直是最小的那个,永远不会被替换掉。
问题是:你不知道当前 4 个是不是最小的 4 个,因为最大值还留在里面!
你得存下所有 M 个元素,再从小顶堆中取前 N 个,等于多维护了不必要的 M - N 个元素,效率大大降低。
总结口诀:
🔹 找最小的 N 个数 → 用大小为 N 的大顶堆,因为你要踢走大的。
🔹 找最大的 N 个数 → 用大小为 N 的小顶堆,因为你要踢走小的。