目录
题目描述
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[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 范围内,你将如何优化你的算法?
思路
这是一道超级经典的题目,相信老师讲数据结构"优先队列"时应该都讲了这一数据结构在中位数上的运用.我么为何会想到优先队列呢?
中位数,如果分为两段,前半段的top()一定是小于后半段的top(),我们分别设置一个大根堆一个小根堆,大根堆的top指向前半段的最大值,小根堆的top()指向后半段的最小值,每次加入num时动态调整两个数据结构,就能维持大根堆的top和小根堆的top的正确指向.
具体情况分为以下几类(规定大根堆为l,小根堆为r):
1.两个堆都是空的,我们直接插入到大根堆l.
2.大根堆l不是空的,小根堆r是空的.
①.当num>l.top时,直接r.push(num)
②.当num<=l.top时,我们先让l.top()出来(pop操作前使用temp保存),再令r.push(temp),l.push(num),维持l.top()<r.top()且l的所有节点都小于等于r.top()的规定.
3.都不是空且l.size()>r.size()
①.num>=r.top(),直接r.push(num)
②.<一>num>=l.top(),直接r.push(num)
<二>num<l.top(),我们暂存l.top(),l.pop(),r.push(temp),l.push(num)
4.都不是空且l.size()==r.size()
①.num<=l.top(),直接l.push(num)
②.<一>num>l.top()&&num<=r.top(),也是l.push(num)
<二>num>r.top()时,l.push(r.top()). r.pop(); r.push(num)
注:在考虑push到哪里时要注意,我们要考虑num==l,top()或者num==r.top()时到底应该在size1<size,size1==size2排列组合的情况,多想一想,拿笔模拟一下就会明白很多.上述思路相对于题解虽然显得啰嗦,但是也比题解更容易理解一些.
最后附上AC代码:
代码
class MedianFinder {
public:
/** initialize your data structure here. */
priority_queue<int, vector<int>, greater<int>> r; //小根堆
priority_queue<int, vector<int>, less<int>> l; //大根堆
MedianFinder() {}
void addNum(int num) {
int size1 = l.size();
int size2 = r.size();
int temp;
if (size1 > size2) { //奇数情况,插入变偶数
if (size2 == 0) {
temp = l.top();
if (temp > num) { //右为空
l.pop();
l.push(num);
r.push(temp);
}
else r.push(num);
}
else {
if (num >= r.top()) {
r.push(num);
}
else if (num < r.top()) {
if (num > l.top()) {
r.push(num);
}
else {
temp = l.top();
l.pop();
l.push(num);
r.push(temp);
}
}
}
}
else if (size1==size2&&size1==0) {
l.push(num);
}
else if(size1==size2&&size1!=0){ //偶数情况,插入变奇数
if (num <= l.top()) {
l.push(num);
}
else {
if (num <= r.top()) {
l.push(num);
}
else {
temp = r.top();
r.pop();
l.push(temp);
r.push(num);
}
}
}
}
double findMedian() {
int size1 = l.size();
int size2 = r.size();
if (size1 > size2) {
return l.top();
}
return ((double)l.top() + (double)r.top())/2.0;
}
};
小结
中位数相关的题目我们要联想到优先队列,在不同情况下的模拟要仔细详尽.