Top K 问题是一类经典算法问题,通常要求从一个数据集中找出最大(或最小)的 K 个元素。堆(Heap)是解决这类问题的高效数据结构。本文将详细讲解堆的原理、Top K 问题的解法,并提供代码实现(C++/Python)。
1. 堆(Heap)简介
堆是一种完全二叉树,满足以下性质:
-
最小堆(Min Heap):父节点的值 ≤ 子节点的值。
-
最大堆(Max Heap):父节点的值 ≥ 子节点的值。
堆的典型操作:
操作 | 时间复杂度 | 说明 |
---|---|---|
insert(x) | O(log n) | 插入元素 |
pop() | O(log n) | 删除堆顶元素 |
peek() | O(1) | 查看堆顶元素 |
2. Top K 问题的常见解法
方法 1:排序法(暴力解法)
-
步骤:
-
对所有数据进行排序(升序或降序)。
-
取前 K 个元素。
-
-
时间复杂度:
-
O(n log n)
(排序时间)。
-
-
适用场景:
-
数据量较小时可用,但效率较低。
-
方法 2:堆(Heap)优化
-
步骤:
-
维护一个大小为 K 的堆:
-
找最大的 K 个元素 → 最小堆(堆顶是最小值,可以快速淘汰更小的元素)。
-
找最小的 K 个元素 → 最大堆(堆顶是最大值,可以快速淘汰更大的元素)。
-
-
遍历数据,动态调整堆。
-
-
时间复杂度:
-
O(n log K)
(遍历数据 + 堆调整)。
-
-
空间复杂度:
-
O(K)
(堆的大小)。
-
3. 代码实现
(1)C++ 实现(最大堆找 Top K 最小元素)
cpp
#include <vector> #include <queue> using namespace std; vector<int> topKSmallest(vector<int>& nums, int k) { if (k <= 0 || nums.empty()) return {}; // 最大堆(堆顶是最大的元素) priority_queue<int> maxHeap; for (int num : nums) { maxHeap.push(num); if (maxHeap.size() > k) { maxHeap.pop(); // 移除最大的元素,保留较小的 K 个 } } vector<int> res; while (!maxHeap.empty()) { res.push_back(maxHeap.top()); maxHeap.pop(); } return res; }
(2)Python 实现(最小堆找 Top K 最大元素)
python
import heapq def topKLargest(nums, k): if k <= 0 or not nums: return [] # 最小堆(堆顶是最小的元素) minHeap = [] for num in nums: heapq.heappush(minHeap, num) if len(minHeap) > k: heapq.heappop(minHeap) # 移除最小的元素,保留较大的 K 个 return minHeap
4. 不同场景下的 Top K 问题
问题 | 解法 |
---|---|
前 K 大的数 | 最小堆(堆顶是最小值,淘汰更小的数) |
前 K 小的数 | 最大堆(堆顶是最大值,淘汰更大的数) |
第 K 大的数 | 最小堆(堆顶就是第 K 大的数) |
第 K 小的数 | 最大堆(堆顶就是第 K 小的数) |
5. 复杂度对比
方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
---|---|---|---|
排序法 | O(n log n) | O(1) 或 O(n) | 数据量小 |
堆优化 | O(n log K) | O(K) | 数据量大,仅需部分排序 |
快速选择(Quickselect) | O(n) (平均) | O(1) | 仅需第 K 大/小,不需要全部排序 |
6. 总结
-
堆(Heap) 是解决 Top K 问题的高效数据结构。
-
最小堆 适合求 最大的 K 个元素(堆顶是最小值,可以快速淘汰更小的数)。
-
最大堆 适合求 最小的 K 个元素(堆顶是最大值,可以快速淘汰更大的数)。
-
时间复杂度
O(n log K)
,比排序法O(n log n)
更高效。