链接:https://leetcode-cn.com/problems/find-median-from-data-stream/
题目描述
果果念
这次的感觉比上次好一些了,因为这里想到了队列,但是只想到了单队列,即一个小顶堆的队列。这里使用了两个队列,一个大顶堆,存储的是较小的元素;一个小顶堆,存储的较大的元素。在进行中位数判断的时候,只需要比较两者元素的大小即可判断。
addNum 函数的复杂度为O(logn);
findMedian 函数的复杂度为 O(1)
空间复杂度:O(n)
进阶
如果数据流中所有整数都在 0 到 100 范围内,你将如何优化你的算法?
可以使用建立长度为 101101 的桶,每个桶分别统计每个数的出现次数,同时记录数据流中总的元素数量,每次查找中位数时,先计算出中位数是第几位,从前往后扫描所有的桶得到答案。
这种做法相比于对顶堆做法,计算量上没有优势,更多的是空间上的优化。
如果数据流中 99% 的整数都在 0 到 100 范围内,你将如何优化你的算法?
和上一问解法类似,对于 11% 采用哨兵机制进行解决即可,在常规的最小桶和最大桶两侧分别维护一个有序序列,即建立一个代表负无穷和正无穷的桶。
上述两个进阶问题的代码如下,但注意由于真实样例的数据分布不是进阶所描述的那样(不是绝大多数都在 [0,100][0,100] 范围内),所以会 TLE。
代码
class MedianFinder {
public:
priority_queue<int,vector<int>,less<int>> numsBTS;//大顶堆
priority_queue<int,vector<int>,greater<int>> numsSTB;//小顶堆,大元素
int length;//总长度
MedianFinder() {
length=0;
}
void addNum(int num) {
length++;
if(numsSTB.size()==0||num>numsSTB.top()){
numsSTB.push(num);
}else{
numsBTS.push(num);
}
int length1=numsBTS.size();
int length2=numsSTB.size();
if(length2-length1>1){
//小顶堆的元素多于大顶堆的元素(>1)
numsBTS.push(numsSTB.top());
numsSTB.pop();
}else if(length1-length2>1){
numsSTB.push(numsBTS.top());
numsBTS.pop();
}
}
double findMedian() {
if(numsBTS.size()==numsSTB.size()){
return (double)(numsBTS.top()+numsSTB.top())/2;
}else{
//返回元素数量多的优先队列
return numsBTS.size()>numsSTB.size()?numsBTS.top():numsSTB.top();
}
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/
参考链接
- 啊这,一道找中位数的算法题把东哥整不会了…
- https://leetcode-cn.com/problems/find-median-from-data-stream/solution/gong-shui-san-xie-jing-dian-shu-ju-jie-g-pqy8/