295. 数据流的中位数
· 约 2 分钟阅读 · – 次阅读
leetcode
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。 double findMedian() - 返回目前所有元素的中位数。 示例:
addNum(1) addNum(2) findMedian() -> 1.5 addNum(3) findMedian() -> 2 进阶:
如果数据流中所有整数都在 0 到 100 范围内,你将如何优化你的算法? 如果数据流中 99% 的整数都在 0 到 100 范围内,你将如何优化你的算法?
两个堆
时间复杂度主要是优先队列调整的时间O(logn) 空间复杂度O(n)
class MedianFinder {
// 建立一个大顶堆 一个小顶堆,大顶堆存储小于中位数的数,小顶堆存储大于中位数的数
// 如 bigHeap存储1-100,smallHeap存储101-200
// 保证大顶堆的数比小顶堆的数多的不超过1
priority_queue<int> bigHeap;
priority_queue<int, vector<int>, greater<int>> smallHeap;
public:
MedianFinder() {
}
void addNum(int num) {
if (bigHeap.empty()) {
bigHeap.emplace(num);
return;
}
if (num >= bigHeap.top()) {
smallHeap.emplace(num);
if (smallHeap.size() > bigHeap.size()) {
bigHeap.emplace(smallHeap.top());
smallHeap.pop();
}
}else{
bigHeap.emplace(num);
if (bigHeap.size() > smallHeap.size()+1){
smallHeap.emplace(bigHeap.top());
bigHeap.pop();
}
}
}
double findMedian() {
// 左右堆的大小一样,即数的个数为偶数
if (bigHeap.size() == smallHeap.size()){
return bigHeap.top() + (smallHeap.top()-bigHeap.top())/2.0;
}else{
return bigHeap.top();
}
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/
有序集合+双指针
时间复杂度同样是O(logn)
class MedianFinder {
multiset<int> nums;
multiset<int>::iterator left, right;
public:
MedianFinder() : left(nums.end()), right(nums.end()) {}
void addNum(int num) {
auto n = nums.size();
nums.insert(num);
if (n == 0) {
left = right = nums.begin();
}else if (n&1) { //集合元素个数原本为奇数
if (num >= *right){
right++;
}else{
left--;
}
}else{ //原本为偶数,这时再加一个数,则left和right会指向中间同一个数
// 有序集合的特点是,如果数相等,新数会在旧数的右边
if (num >= *right){
left++;
}else{
right--;
left = right;
}
}
}
double findMedian() {
return (*left+*right)/2.0;
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/
进阶 11 如果数据流中所有整数都在 00 到 100100 范围内,那么我们可以利用计数排序统计每一类数的数量,并使用双指针维护中位数。
进阶 22 如果数据流中 99%99% 的整数都在 00 到 100100 范围内,那么我们依然利用计数排序统计每一类数的数量,并使用双指针维护中位数。对于超出范围的数,我们可以单独进行处理,建立两个数组,分别记录小于 00 的部分的数的数量和大于 100100 的部分的数的数量即可。当小部分时间,中位数不落在区间 [0,100][0,100] 中时,我们在对应的数组中暴力检查即可。