汇芳书院

专注计算机视觉、机器学习、分布式计算等领域, 兼聊投资、写作、生活

0%

295. 数据流的中位数

中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。

例如,

[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 范围内,你将如何优化你的算法?


两个堆

时间复杂度主要是优先队列调整的时间O(logn)
空间复杂度O(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class MedianFinder {
// 建立一个大顶堆 一个小顶堆,大顶堆存储小于中位数的数,小顶堆存储大于中位数的数
// 如 bigHeap存储1-100,smallHeap存储101-200
// 保证大顶堆的数比小顶堆的数多的不超过1
priority_queue<int> bigHeap;
priority_queue<int, vector<int>, greater<int>> smallHeap;
public:
MedianFinder() {

}

void addNum(int num) {
if (bigHeap.empty()) {
bigHeap.emplace(num);
return;
}
if (num >= bigHeap.top()) {
smallHeap.emplace(num);
if (smallHeap.size() > bigHeap.size()) {
bigHeap.emplace(smallHeap.top());
smallHeap.pop();
}
}else{
bigHeap.emplace(num);
if (bigHeap.size() > smallHeap.size()+1){
smallHeap.emplace(bigHeap.top());
bigHeap.pop();
}
}
}

double findMedian() {
// 左右堆的大小一样,即数的个数为偶数
if (bigHeap.size() == smallHeap.size()){
return bigHeap.top() + (smallHeap.top()-bigHeap.top())/2.0;
}else{
return bigHeap.top();
}

}
};

/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/

有序集合+双指针

时间复杂度同样是O(logn)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class MedianFinder {
multiset<int> nums;
multiset<int>::iterator left, right;
public:
MedianFinder() : left(nums.end()), right(nums.end()) {}

void addNum(int num) {
auto n = nums.size();
nums.insert(num);
if (n == 0) {
left = right = nums.begin();
}else if (n&1) { //集合元素个数原本为奇数
if (num >= *right){
right++;
}else{
left--;
}
}else{ //原本为偶数,这时再加一个数,则left和right会指向中间同一个数
// 有序集合的特点是,如果数相等,新数会在旧数的右边
if (num >= *right){
left++;
}else{
right--;
left = right;
}
}

}

double findMedian() {
return (*left+*right)/2.0;


}
};

/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/

进阶 11
如果数据流中所有整数都在 00 到 100100 范围内,那么我们可以利用计数排序统计每一类数的数量,并使用双指针维护中位数。

进阶 22
如果数据流中 99%99% 的整数都在 00 到 100100 范围内,那么我们依然利用计数排序统计每一类数的数量,并使用双指针维护中位数。对于超出范围的数,我们可以单独进行处理,建立两个数组,分别记录小于 00 的部分的数的数量和大于 100100 的部分的数的数量即可。当小部分时间,中位数不落在区间 [0,100][0,100] 中时,我们在对应的数组中暴力检查即可。

坚持原创分享,您的支持将鼓励我继续创作

欢迎关注我的其它发布渠道

------------- 本文结束,感谢阅读 如有问题可留言交流 -------------