题目描述
传送门
数据流的中位数:中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。
- 例如 arr = [2,3,4] 的中位数是 3 。
- 例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5 。
目标:实现 MedianFinder 类
- MedianFinder() 初始化 MedianFinder 对象。
- void addNum(int num) 将数据流中的整数 num 添加到数据结构中。
- double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。
示例:
输入 [“MedianFinder”, “addNum”, “addNum”, “findMedian”, “addNum”,“findMedian”] [[], [1], [2], [], [3], []]
输出 [null, null, null, 1.5,null, 2.0]
解释 MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1); // arr = [1]
medianFinder.addNum(2); //arr = [1, 2]
medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)
medianFinder.addNum(3); // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0
注意:
- -105 <= num <= 105
- 在调用 findMedian 之前,数据结构中至少有一个元素
- 最多 5 * 104 次调用 addNum 和 findMedian
我的解法
class MedianFinder {
private:
vector<int> vect1;
vector<int> vect2;
public:
MedianFinder() {
}
void addNum(int num) {
vect1.push_back(num);
}
double findMedian() {
// 搜索中位数前,先对容器中的元素进行排序
sort(vect1.begin(),vect1.end());
// 容器中没有元素
if(vect1.size() == 0){
return 0.0;
}
// 容器中有元素
else{
// 元素数量为奇数
if(vect1.size() % 2 != 0){
int index = vect1.size() / 2;
while(index--){
vect2.push_back(vect1.back());
vect1.pop_back();
}
// 获得中位数
double res = vect1.back();
while(!vect1.empty()){
vect2.push_back(vect1.back());
vect1.pop_back();
}
// 将辅助容器中的元素返回存储容器中
while(!vect2.empty()){
vect1.push_back(vect2.back());
vect2.pop_back();
}
return res;
}
else{
// 元素数量为偶数
int index = vect1.size() / 2 - 1;
while(index--){
vect2.push_back(vect1.back());
vect1.pop_back();
}
// 获取两个求中位数的值
double num1 = vect1.back();
vect2.push_back(vect1.back());
vect1.pop_back();
double num2 = vect1.back();
vect2.push_back(vect1.back());
vect1.pop_back();
while(!vect1.empty()){
vect2.push_back(vect1.back());
vect1.pop_back();
}
double res = (num1 + num2) / 2.0;
// 将辅助容器中的元素返回存储容器中
while(!vect2.empty()){
vect1.push_back(vect2.back());
vect2.pop_back();
}
return res;
}
}
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/
结果
超出时间限制
分析
时间复杂度:
添加元素:O(1)
查询中位数:O(nlogn),由于需要先对容器里的元素进行排序再进行中位数的查询
空间复杂度:
需要两个额外的容器来完成中位数的查询,O(n)
官方题解
class MedianFinder {
public:
// 创建一个最大堆,最大的元素位于队列的队首
priority_queue<int, vector<int>, less<int>> queMin;
// 创建一个最小堆,最小的元素位于队列的队首
priority_queue<int, vector<int>, greater<int>> queMax;
MedianFinder() {
}
void addNum(int num) {
// 添加的元素小于中位数,则将元素添加到小于中位数的队列
if(queMin.size() == 0 || num <= queMin.top()){
queMin.push(num);
// 用于保持大于中位数的队列和小于中位数的队列之间的大小不超过1,从而保证中位数的位置
if(queMax.size() + 1 < queMin.size()){
queMax.push(queMin.top());
queMin.pop();
}
} else{
queMax.push(num);
// 用于保持大于中位数的队列和小于中位数的队列之间的大小不超过1,从而保证中位数的位置
if(queMax.size() > queMin.size()){
queMin.push(queMax.top());
queMax.pop();
}
}
}
double findMedian() {
if(queMin.size() > queMax.size()){
return queMin.top();
}else{
return (queMax.top() + queMin.top()) / 2.0;
}
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/
分析
时间复杂度:
添加元素:O(logn),完成建堆操作
查找中位数:O(1)
空间复杂度:
O(n),主要为有序集合的开销
查漏补缺
优先队列
在 C++ 中,优先队列是一种特殊的队列数据结构,它的元素按照一定的优先级进行排序。在标准库中,优先队列的实现通常基于堆数据结构,通过堆来维护元素的优先级。
priority_queue<int, vector<int>, less<int>> queMin;
创建了一个名为 queMin 的最大堆(Max Heap)的优先队列(priority_queue)。在 C++ 中,默认情况下,priority_queue 是一个最大堆,也就是说,队列中的最大元素位于队首。
- int: 表示 priority_queue 中存储的元素类型是整数(int)。
- vector: 表示使用 std::vector 作为底层容器来存储优先队列的元素。
- less: 表示使用 std::less 比较函数对象来确定堆的顺序。在这种情况下,less 表示最大堆,即队首元素是最大的。同理,great表示最小堆,即队首元素为最小的。