一、题目描述
题目要求
设计一个可以持续接收数据流、快速返回第 K 大元素 的类 KthLargest,注意定义:将所有数字降序排序后,位于第 k 个位置的数,允许存在重复数字。 需要实现两个核心方法:
- 构造函数
KthLargest(int k, vector<int>& nums):给定 k 和初始数字流完成初始化; int add(int val):新增一个数字到数据流,返回当前全局第 k 大的值。
示例
输入:k=3,初始数组 [4,5,8,2] 依次调用 add(3)、add(5)、add(10)、add(9)、add(4) 输出:4,5,5,8,8 解释:
- 初始数据流:
[4,5,8,2],前 3 大数字[4,5,8],第 3 大为 4; - 添加 3 后,前 3 大不变,返回 4;
- 添加 5 后,前 3 大
[5,5,8],返回 5; - 添加 10 后,前 3 大
[5,8,10],返回 5; - 添加 9 后,前 3 大
[8,9,10],返回 8; - 添加 4 后,前 3 大不变,返回 8。
二,最优解法:固定容量小根堆(最小堆)
核心原理
我们只需要全局最大的 k 个数字,不需要存储全部数据:
- 使用小根堆,堆内最多保存 k 个元素;
- 堆的特性:堆顶是堆中最小值;
- 堆内存放当前前 k 大数字,堆顶天然就是全局第 k 大元素。
执行流程
- 初始化阶段:遍历所有初始数字,逐个入堆;若堆长度超过 k,弹出堆顶最小值(该数不属于前 k 大);
- add 新增数字:
- 将新数字压入堆;
- 若堆大小 > k,弹出堆顶最小元素;
- 直接返回堆顶,即为当前第 k 大值。
为什么不用大根堆?
如果使用大根堆,会存储全部数据流,空间复杂度 O(n);且每次取第 k 大需要遍历堆,效率低下。小根堆仅存 k 个元素,空间、时间双重最优。
cpp
class KthLargest {
priority_queue<int ,vector<int>,greater<int>>heap;
int _k;
public:
KthLargest(int k, vector<int>& nums) {
_k=k;
for(auto e:nums)
{
heap.push(e);
if(heap.size()>_k)
heap.pop();
}
}
int add(int val) {
heap.push(val);
if(heap.size()>_k)
heap.pop();
return heap.top();
}
};
/**
* Your KthLargest object will be instantiated and called as such:
* KthLargest* obj = new KthLargest(k, nums);
* int param_1 = obj->add(val);
*/