文章目录
1. 题目来源
链接:I. 滑动窗口的最大值
链接:II. 队列的最大值
来源:LeetCode——《剑指-Offer》专项
2. 题目说明
3. 题目解析 — I. 滑动窗口的最大值
方法一:模拟+单调队列+常规解法
实际考查 单调队列,若采用暴力法,需要
的时间复杂度找到滑动窗口的最大值,对于长度为 n
的输入数组,暴力求解算法复杂度为
。
我们能发现,这个滑动窗口就是一个队列,满足先进先出的特性,而我们需要找到队列的最大值,该问题就能解决了。
我们在 [剑指-Offer] 30. 包含min函数的栈(边界情况,代码优化) 采用 的时间得到了栈的最大值,并且在 [剑指-Offer] 9. 用两个栈实现队列(栈、队列、模拟) 中,也用栈模拟实现了队列。那么这个问题至此,我们能解决了。
下面来介绍采用 deque
的做法思想:
- 插入前
k
个数据- 首先若
deque
为空,那么就直接尾插进第一个数据 - 插入第二个数据时,队列非空,若该数字比队尾数字大,那么队尾数字就不可能成为滑动窗口的最大值,那么就将队尾数据进行出队操作。持续上述操作,直到队列为空或者遇到队列中比它大的数据,再将数据插入。其实是寻找前
k
个数据中的最大值,作为队首元素
- 首先若
- 插入
k+1
至后面所有数据- 在插入
k+1
的时候,首先将队列中上一个滑动窗口的最大值放进创建的vt
中,即需要将队首元素返回 - 如果此时队列不为空,并且待插入数据比队尾元素大的话,说明队尾元素不可能是该滑动窗口的最大值了,即需要队尾出队。持续上述操作,直到队列为空或者遇到队列中比它大的数据,再将数据插入。其实是寻找前
k
个数据中的最大值,添加成为队列元素 - 到达此步需要进行队首元素是否在当前滑动窗口的判断。即如果此时队列不为空,队首元素是最大值,但是可能是上一个滑动窗口的最大值,其根本就不在当前窗口内,需要将队首数据进行出队。判断的方式比较简单,就是单纯的待入队元素下标减去队首下标,查看其差值是否大于
k
即可。所以为了方便,我们在deque
存放的是数组的下标。 这个需要额外注意
- 在插入
- 最后将队首元素添加进入数组
vt
,并返回vt
即可
这个其实就是一般的 单调队列 数据结构的抽象。
参见代码如下:
// 执行用时 :12 ms, 在所有 C++ 提交中击败了99.62%的用户
// 内存消耗 :17.6 MB, 在所有 C++ 提交中击败了100.00%的用户
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
if (nums.size() < k or k <= 0) return {};
vector<int> vt;
deque<int> dq;
for (int i = 0; i < k; ++i) {
while (!dq.empty() and nums[i] >= nums[dq.back()]) dq.pop_back();
dq.push_back(i);
}
for (int i = k; i < nums.size(); ++i) {
vt.push_back(nums[dq.front()]);
while (!dq.empty() and nums[i] >= nums[dq.back()]) dq.pop_back();
if (!dq.empty() and dq.front() <= i - k) dq.pop_front();
dq.push_back(i);
}
vt.push_back(nums[dq.front()]);
return vt;
}
};
4. 题目解析 — II. 队列的最大值
方法一:模拟+单调队列+常规解法
采用上个问题滑动窗口的思想,创建 queue
再以辅助 deque
进行存储最大值,很容易实现这个带 max
函数的队列,主要思路如下:
queue
就是正常的队列,负责push
和pop
deque
用来存放最大值- 如果新的
value > deque.back()
,那么deque
一直进行pop_back
操作,直到尾端的值大于等于value
或者为空 - 再将
value
压入deque
的尾部 - 每次取
max_value
,返回deque
首部的值 - 当
queue
进行pop
操作时,如果que
首部的值等于deque
首部的值,那么deque
同样需要进行pop_front
操作。否则仅需queue
出队即可,deque
队首元素仍是queue
的最大值
这是一个模拟实现,也是单调队列基础。
参见代码如下:
// 执行用时 :180 ms, 在所有 C++ 提交中击败了36.69%的用户
// 内存消耗 :50.3 MB, 在所有 C++ 提交中击败了100.00%的用户
class MaxQueue {
public:
MaxQueue() {
}
int max_value() {
return que.empty() ? -1 : dq.front();
}
void push_back(int value) {
que.push(value);
while (!dq.empty() and dq.back() < value) dq.pop_back();
dq.push_back(value);
}
int pop_front() {
if(que.empty()) return -1;
int t = que.front();
que.pop();
if(t == dq.front()) dq.pop_front();
return t;
}
private:
queue<int> que;
deque<int> dq;
};
/**
* Your MaxQueue object will be instantiated and called as such:
* MaxQueue* obj = new MaxQueue();
* int param_1 = obj->max_value();
* obj->push_back(value);
* int param_3 = obj->pop_front();
*/