1 单调队列
单调队列,顾名思义,是维护一个单调递增的或单调递减的队列,比较容易地想到可以使用deque来维护这个单调队列,这是因为deque可以在头尾进行插入和删除。这里我们可以维护一个单调递减的队列。插入元素时,将队列中所有比它小的元素删除即可。这里我们只封装push,pop,getmax这三个方法,如下,就可以看出为什么是一个单调递减队列。数组元素是:[1,3,-1,-3,5,3,6,7]
class MonotonicQueue
{
public:
void push(int x)
{
//保持单调性,删除比新元素x小的所有元素
while(!dq.empty()&&x>dq.back())
dq.pop_back();
dq.push_back(x);
}
int getmax()
{
return dq.front();
}
void pop()
{
dq.pop_front();
}
private:
deque<int> dq;
};
当然,如果想维护一个单调递增的队列,我们只需在push的时候,将所有比新元素大的元素删除,随后在push新元素。
2 leetcode239 滑动窗口最大值
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
如果是暴力解法的话,时间复杂度为
O
(
N
K
)
O(NK)
O(NK),空间复杂度为
O
(
1
)
O(1)
O(1)
vector<int> maxSlidingWindow(const vector<int>& nums, unsigned int k)
{
if(nums.size()==0)
return {};
vector<int> ret;
//一共nums.size()-k+1个滑动窗口
for(int i=0;i<nums.size()-k+1;i++)
{
int Max=-1;
for(int j=i;j<k+i;j++)
{
Max=max(nums[j],Max);
}
ret.push_back(Max);
}
return ret;
}
如果k很大的时候,时间复杂度就是
O
(
n
2
)
O(n^2)
O(n2)。
我们可以使用一个单调递减的队列,这样我们就不需要每次进行k-1次比较,时间复杂度降到
O
(
n
)
O(n)
O(n),空间复杂度为
O
(
n
)
O(n)
O(n)。
class MonotonicQueue
{
public:
void push(int x)
{
//保持单调性,删除比新元素x小的所有元素
while(!dq.empty()&&x>dq.back())
dq.pop_back();
dq.push_back(x);
}
int getmax()
{
return dq.front();
}
void pop()
{
dq.pop_front();
}
private:
deque<int> dq;
};
class Solution {
public:
//单调队列优化
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
if(nums.size()==0)
return {};
MonotonicQueue mq;
vector<int> ans;
for(int i=0;i<nums.size();i++)
{
mq.push(nums[i]);
if(i-k+1>=0)
{
ans.push_back(mq.getmax());
if(mq.getmax()==nums[i-k+1])
mq.pop();
}
}
return ans;
}
};
当然,我们也不必特地维护一个单调队列,直接在push或pop时判断即可,只是这样更好理解。