原题地址:https://leetcode-cn.com/problems/find-median-from-data-stream/
题目描述:
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[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 范围内,你将如何优化你的算法?
解题方案:
设计题型。这里使用了C++STL中的upper_bound()函数。
upper_bound(a.begin(),a.end(),x)返回的是迭代器,upper_bound(a+i,a+j,x)-a返回的是第一个大于x的数的坐标。
在这里算是在指定位置插入元素。upper_bound()和insert()函数组合使用。
代码:
class MedianFinder {
public:
/** initialize your data structure here. */
MedianFinder() {
}
void addNum(int num) {
auto it = upper_bound(nums.begin(), nums.end(), num);
nums.insert(it, num);
}
double findMedian() {
int n = nums.size();
if(n % 2 == 0)
return 1.0 * (nums[n / 2 - 1] + nums[n / 2]) / 2;
else
return nums[n / 2];
}
vector<int> nums;
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
不需要所有的数据有序,只需要中间的值,很自然想到把列表分成两半,分别用两个数据结构来保存,比如[1,3,4,5,6,7],对于这个列表,可以存为[1,3,4]和[5,6,7],那么只需要中间的值就可以得出中位数了。
可以采用优先级队列,priority_queue:左侧为数字越大优先级越高,右侧为数字越小优先级越高。那么取出两个优先级的top值即可。
代码:
class MedianFinder {
public:
/** initialize your data structure here. */
MedianFinder() {
}
void addNum(int num) {
left.push(num);
if(left.size() - right.size() > 1) {
right.push(left.top());
left.pop();
}
if(right.size() > 0 && left.top() > right.top()){
int tmp = left.top();
left.pop();
left.push(right.top());
right.pop();
right.push(tmp);
}
}
double findMedian() {
if(left.size() == right.size())
return 1.0 * (left.top() + right.top()) / 2;
else
return left.top();
}
priority_queue<int, vector<int>,greater<int>> right;
priority_queue<int, vector<int>,less<int>> left;
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/