题面
解法1
思路分享
- 其实可以说是一道模拟题吧
- 最常规的思路如下:
- 结合题意,Insert()方法读取数据就是:逐个把数据流里面的数压入vector容器去处理;
- GetMedian()方法获取当前读取数据...就是:把当前容器中的数按照要求处理返回中位数
- 这个要求就是排好序后照常返回中位数
复杂度分析
- 时间复杂度:寻找中位数操作中,快排复杂度O(nlogn),公式计算中位数复杂度为O(1), 故总体复杂度为O(nlogn)
- 空间复杂度:录入数据过程中,每个数据只操作一次,复杂度为O(n)
代码呈现
class Solution {
public:
vector<int> v;
void Insert(int num) {
v.push_back(num); //把num元素添加到容器中
}
double GetMedian() {
sort(v.begin(),v.end());
int len = v.size();
if(len%2 != 0)
return v[(len-1)/2]*1.0; //注意vector从0开始,且输出为浮点数
else
return (v[len/2]+v[len/2-1])*1.0/2;
}
};
解法2
思路分享
构造&大小顶堆
定义两个堆,一个大顶堆Lmax
, 一个小顶堆Rmin
,分别存放位于中位数左边和中位数自身,位于中位数右边的数据,这样只要问两个堆的堆顶即可获得中位数的结果,由中位数奇偶性质可知:
-
当已添加的数据个数为奇数时,小顶堆的数据个数比大顶堆多一个,此时若要再添加一个数据,必定添加在小顶堆
Rmin
上,但不知道当前添加的数是否大于中位数,故先插入Lmax
然后将Lmax
中的最大数放入Rmin
中,同时删除Lmax
中的最大数,即删除堆顶。 -
当已添加的数据个数为偶数时,小顶堆的数据个数等于大顶堆,此时若再添加一个数据,必定添加在大顶堆
Lmax
上,添加的手段和前者相同。
这样我们便将数据进行了分隔,然后通过O(1)的复杂度访问堆顶即可计算出中位数。
假定录入数据流为[5,3,2,1,4],过程模拟如下:
1. 添加第1个数据,当前已经添加的个数为0个,偶数个,故添加在Lmax上,先将数据5放入Rmin筛出最小值再放入Lmax,同时删除Lmax的最小值。
2. 添加第2个数据,当前已经添加的个数为1个,奇数个,故添加在Rmin上,先将数据3放入Lmax筛出最大值再放入Rmin,同时删除Rmin的最大值。
以下步骤同理...
复杂度分析
- 时间复杂度:使用堆处理数据,每次添加时间复杂度为O(nlogn)
- 空间复杂度:查询最值复杂度为O(1)
代码呈现
class Solution {
public:
priority_queue<int> Lmax;
priority_queue<int, vector<int>, greater<int> > Rmin;
int cnt = 0; //记录当前已经添加数据的个数
void Insert(int num) {
if(cnt & 1) { //已经添加了奇数个,当前添加至Rmin
Lmax.push(num);
Rmin.push(Lmax.top());
Lmax.pop();
} else { //已经添加了偶数个,当前添加至Lmin
Rmin.push(num);
Lmax.push(Rmin.top());
Rmin.pop();
}
cnt++;
}
double GetMedian() {
if(cnt&1) return Lmax.top(); //奇数
else return (Lmax.top()+Rmin.top())/2.0; //偶数
}
};