相信大家都知道什麼是中位數吧,這題最直觀的方式就是針對每一個 window 去做排序,做完排序就可以找到每個 window 的中位數,但…這很花時間,這樣的時間複雜度是
這題我們提供一個作法,也就是利用 multiset 去做優化,具體上怎麼做呢?我們來看看!
我們要做的就是維護兩個 multiset ,一個是包含中位數的左邊的 left ,另一個是中位數右邊的 right ,並且使 left 的 multiset 會指向我們的中位數,這樣一來我們就可以取 *left
當做我們的中位數,並且隨著 window 移動,我們也可以很快的刪除以及加入元素,這樣的複雜度就會是
要記得 multiset 的底層是紅黑樹,因此在插入以及刪除都是
這邊要注意一個點就是 multiset 在 erase 元素的時候如果是塞 multiset.erase(value)
,這樣會使整個容器內的 value 值都被刪掉,因此如果只要刪除某個 value 的其中一個值,記得是塞 iterator ,例如 multiset.erase(*left.begin())
。
#include <bits/stdc++.h>
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
typedef long long ll;
void balance(multiset<int, greater<int> > &left, multiset<int, less<int> > &right){
while(left.size() > right.size()){
right.insert(*left.begin());
left.erase(left.begin());
}
while(left.size() < right.size()){
left.insert(*right.begin());
right.erase(right.begin());
}
}
int main(){
IOS
int n, k, arr[200005];
cin >> n >> k;
for(int i = 0;i < n;i++) cin >> arr[i];
multiset<int, greater<int> > left;
multiset<int, less<int> > right;
for(int i = 0;i < n;i++){
if(!left.size() || arr[i] <= *left.begin()) left.insert(arr[i]);
else right.insert(arr[i]);
if(i >= k){
if(left.size() && arr[i - k] <= *left.begin())
left.erase(left.find(arr[i - k]));
else right.erase(right.find(arr[i - k]));
}
balance(left, right);
if(i >= k - 1) cout << *left.begin() << ' ';
}
cout << '\n';
return 0;
}