题目描述
思路 multiset
看到困难题,小萌新稍微想了一下想用之前求中位数的方法:大根堆和小根堆,但是代码是在是太长了,只是为了春招没必要那么卷,于是去找了multiset的解法。
set的本质是红黑树:平衡二叉搜索树。而multiset是可以有重复元素的set
由于对stl还处在一知半解的地步,昨晚还在想着set有什么用呢,今天就意识到了。
set里面的数字是可以用迭代器来获得的:配合prev和next
- prev(迭代器,n)
返回迭代器的前n个位置的迭代器。 - next(迭代器,n)
返回迭代器的后n个位置的迭代器。
这样就能指定一个位置,获得有序情况下的第n个数字
ps:迭代器指向的是空间,要获得元素得加上*
得到理所当然的代码
class Solution {
public:
multiset<int> ms;
double get(int& k){
if(k%2) return *(next(ms.begin(), k/2));
else return ((long long)(*(next(ms.begin(), k/2-1)))+(*next(ms.begin(), k/2))) * 0.5;
}
vector<double> medianSlidingWindow(vector<int>& nums, int k) {
for(int i = 0; i < k; i++)ms.insert(nums[i]);
vector<double> ans{
get(k)};
for(int i = k; i < nums.size(); i++){
ms.insert(nums[i]);
ms.erase(ms.find(nums[i-k]));
ans.push_back(get(k));
}
return ans;
}
};
击败9%的人,非常垃圾
时间复杂度O(K*(logK + K))
空间复杂度O(K)
然后就在前10%的题解里面找,清一色都是大根堆小根堆,然后眼前一亮!
// 优先队列
class Solution {
public:
vector<double> medianSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
vector<double> medians;
multiset<int> numset{
nums.begin(), nums.begin()+k};
auto mid = next(numset.begin(), k/2);
for (int i = k; i <= n; i++) {
//如果是k&1=1说明是奇数,结果是(*mid + *mid)/2 = *mid 否则就是 (*mid + *(mid - 1))/2
medians.push_back(((double)*mid + *(next(mid, (k&1) ? 0 : -1)))/2);
//这样第一句最后可以再算一次
if (i == n) break;
numset.insert(nums[i]);
if (nums[i] < *mid) {
--mid;
}
if (nums[i-k] <= *mid) {
++mid;
}
numset.erase(numset.lower_bound(nums[i-k]));
}
return medians;
}
};
好家伙,时间复杂度直接降到O(Klogk)
把调用next的时间变成了
if (nums[i] < *mid) --mid;
if (nums[i-k] <= *mid) ++mid;
根据下一个数字的位置来决定mid迭代器如何维持在中间,省去了O(K)的复杂度取而代之O(1)。
通过这题我对迭代器也有了新的认识:能够指向一个容器的某一个位置,并且根据容器的元素类型的大小来移动自身。