每日一题:滑动窗口中位数

题意

给你一个数组 nums,有一个长度为 k 的窗口从最左端滑动到最右端。窗口中有 k 个数,每次窗口向右移动 1 位。你的任务是找出每次窗口移动后得到的新窗口中元素的中位数,并输出由它们组成的数组。$(n, k \in [1, 100000])$

Solution

用优先队列+延迟删除有点麻烦,可以考虑直接用 multiset 做,本质都是一样的,维护两个大小相同或差一的集合(自动排序)$l 和 r$,$l$ 维护小于等于中位数的值,$r$ 维护大于等于中位数的值,$multiset$ 的优秀特性可以在 $log$ 时间复杂度内进行增删操作。具体细节较多,时间复杂度 $O(nlog(k))$。

Code

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
class Solution {
public:
vector<double> medianSlidingWindow(vector<int>& a, int k) {
multiset<double> l, r;
int n = a.size();
vector<double> res;
for (int i = 0; i < n; i++) {
if (i >= k) { // 需要移除之前已经加入滑动窗口的元素
if (l.count(a[i - k])) l.erase(l.find(a[i - k]));
else r.erase(r.find(a[i - k]));
}
l.insert((double)a[i]);
while (l.size() && r.size() && *(--l.end()) > *r.begin()) { // 左集合的元素不可能大于右集合
l.insert(*r.begin());
r.insert(*(--l.end()));
l.erase(*(--l.end()));
r.erase(*r.begin());
}
while (l.size() - 1 > r.size()) { // 左集合元素最多只会比右集合多一(窗口大小为奇数)
r.insert(*(--l.end()));
l.erase(--l.end());
}
if (i >= k - 1) {
if (k & 1) res.push_back(*(--l.end()));
else res.push_back((*(--l.end()) + *r.begin()) / 2.0);
}
}
return res;
}
};
作者

Benboby

发布于

2021-02-03

更新于

2021-02-03

许可协议

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×