力扣-239-滑动窗口的最大值
居然是道困难题,与之相比,子序列只是道简单题
拆分成两个步骤:
- 维持滑动窗口
- 找出窗口中的最大值
那么能不能将这两个步骤统一呢?
考虑每次都是取走滑动窗口的第一个元素,新插入最后一个元素
- 如果取走元素不是最大元素,这个好说,
maxNum = max(maxNum,newNum)
- 如果取走的就是maxNum,如果没有相同的数字还好说,但是如果窗口中有不止一个maxNum呢?也就是最大值重复的情况
// 不得已扫描一遍整个序列,找出最大值
int findMax(vector<int>& nums, int left, int right) {
int maxNum=nums[left];
for (int i = left+1; i <= right; i++)
maxNum = max(maxNum, nums[i]);
return maxNum;
}
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res(1);
for (int i = 0; i < nums.size(); i++) {
// 初始化滑动窗口
if (i < k) res[0] = max(res[0], nums[i]);
else {
if (nums[i] >= res[i-k]) res.push_back(nums[i]);
else {
// 如果被丢出去的是最大值,但是没法保证剩下的里面没有重复的最大值,所以只能扫描一遍
if (nums[i-k] = res[i - k]) res.push_back(findMax(nums, i-k+1, i));
// 被丢出去的不是最大值,新加入的又没有最大值大,那最大值仍旧是最大值
else res.push_back(res[i - k]);
}
}
}
return res;
}
这是我的第一版代码,很直接的思路,我也知道那个全序列扫描绝对是瓶颈,测试用例也不负所望,给了k=5000的不当人用例,果然难和40几的通过率不是白来的
可能得换个思路才行了
我想到了二维动态规划
// 定义dp[i][j]表示从第i个元素开始,到第j个元素为止的最大值
// 状态转移方程为:横向:dp[i][j] = max(dp[i][j-1],nums[j])
// 纵向为:dp[i][j]=
好像也不对,这么定义的话这是一个上三角,而且也多了很多冗余
官解
优先队列…和单调队列
都听过见过,但也仅限于此
优先队列就是堆,那么它和普通队列有什么区别?
单调队列以前遇到过一次,具体哪个题忘记了
优先队列解法
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;
priority_queue<pair<int,int>> window;
// 好,既然会用优先队列了,就用优先队列解决这个问题
// 但问题是,它改变了队列的原始顺序,我怎么pop第一个元素呢?并不能pop指定元素
// 什么意思?其实并不需要每一个都pop,如果本应pop的元素不是最大值,因为我们只关心最大值,所以pop与否没有影响
// 但是如果待pop元素就是最大值,那么直接pop堆顶就可以了
// 这也是题目提示中的意思,这个队列的大小并不需要是固定的
for (int i = 0; i < nums.size(); i++) {
window.emplace(nums[i],i);
if (i >= k-1) {
while (window.top().second < i - k + 1) window.pop();
res.push_back(window.top().first);
}
}
// 不行,这里不pop其实是对后面有影响的,必须有个机制在某个时间将左边界左侧的元素排除
// 实现这一点是通过保存一个额外的索引来实现的
// 这个时机是在res取之前,保证这个max确实是在窗口中的
return res;
}
空间效率很不理想,时间效率也不算好
根本上是来自于堆的空间占用和堆的调整用时上面,虽然不用自己实现,但是过程消耗是真实存在的
堆排序时间复杂度O(n logn),空间复杂度O(1)
单调队列解法
我去看了单调队列,说实话没看明白,等二刷吧