【LeetCode & 剑指offer刷题】栈与队列题5:59 队列(滑动窗口)的最大值
【LeetCode & 剑指offer 刷题笔记】目录(持续更新中...)
59 队列(滑动窗口)的最大值
题目一:滑动窗口的最大值
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
/*
方法一:暴力法
每个滑动窗口都计算最大值,
O(nk),O(1), k为滑动窗的长度
*/
/*
方法二:用一个双端队列来存各阶段的最大值
用O(1)时间得到滑动窗口的最大值,队列最大长度为k
O(n),O(k)
存索引,这样可以判断窗口位置,从而决定是否移除队首元素
*/
class Solution
{
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int> result;
if(size > num.size() || size < 1) return result;
deque<int> index; //双端队列
//队列中存的元素中,队首元素为当前窗口下的最大值,队首外的元素为最大值之后的次大值(队列中索引指向元素为从大到小,均为局部极大值点)
for(unsigned int i = 0; i < num.size(); i++)
{
while(!index.empty() && num[i] >= num[index.back()])
index.pop_back(); //从队尾依次弹出队列中比当前num值小的元素,同时也能保证队列首元素为当前窗口最大值下标
index.push_back(i); //插入当前索引值,因为可能为其他窗口下的最大值
if(!index.empty() && i - index.front()+1 > size)
index.pop_front();//如果队首索引所指向的元素已经不在窗口中了就弹出
if(i >= size-1)//当有完整的窗口覆盖后才开始push最大值
result.push_back(num[index.front()]);
}
return result;
}
};
题目二:队列的最大值
请定义一个队列并实现函数max得到队列里的最大值,要求函数max、push_back和pop_front的时间复杂度都是O(1)。
思路:
-
同上一题相同,我们要寻找队列的最大值,相当与将滑动窗口设置为整个队列。
-
这里需要使用两个队列,一个队列用来保存入队的数据,一个队列用来保存队列的当前最大值。
-
同时需要注意出队操作,数据队列出队的同时需要判断其索引是否和当前最大值队列首部索引相同,如果相同则同时也将最大值队列头部出队。
比较:
-
min栈的实现:队列中是尾部插入,首部删除(滑动窗口是这种情况,故用队列较好),而min栈较方便,在一端插入和删除
-
最大值队列在push当前值之前,需将之前较小的值出队处理(从队尾开始判断),队首元素即为当前队列的最大值
template<typename T> class QueueWithMax {
public:
QueueWithMax() :currentIndex(0) {}
void push_back(T number) { //该函数无需考虑队首索引所指向的元素已经不在窗口中了就弹出的情况,因为滑动窗口相当于整个队列
while (!maximums.empty() && number >= maximums.back().number) //剔除最大值队列中较当前值小的元素
maximums.pop_back(); //保证最大值队列中存的是主极大与次极大
InternaData internaData = {number, currentIndex};
data.push_back(internaData);
maximums.push_back(internaData); //push当前元素,有可能其为之后窗口的最大值
++currentIndex;
}
void pop_front() {
if (maximums.empty())
throw new exception("queue is empty.");
if (maximums.front().index == data.front().index) //如果弹出的元素即为当前最大值,注意把最大值队列队首元素也出队
maximums.pop_front();
data.pop_front();
}
T max() const {
if (maximums.empty())
throw new exception("queue is empty.");
return maximums.front().number; //最大值队列队首即为当前窗口最大值
}
private:
struct InternaData //自定义数据结构(数+索引)
{
T number;
int index;
};
deque<InternaData> data; //存储队列数据
deque<InternaData> maximums; //窗口相当于整个队列
int currentIndex;
};