题目
滑动窗口的最大值
给定一个数组和滑动窗口的大小,请找出所有滑动窗口里的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,它们的最大值分别为{4,4,6,6,6,5}
思路
1、一个滑动窗口可以看成一个队列。当窗口滑动时,处于窗口的第一个数字被删除,同时在窗口的末尾添加一个新的数字。这符合队列“先进先出”特性。
2、题意:
数组中的滑动窗口 | 滑动窗口中的最大值 |
---|---|
[2,3,4],2,6,2,5,1 | 4 |
2,[3,4,2],6,2,5,1 | 4 |
2,3,[4,2,6],2,5,1 | 6 |
2,3,4,[2,6,2],5,1 | 6 |
2,3,4,2,[6,2,5],1 | 6 |
2,3,4,2,6,[2,5,1] | 5 |
3、只把有可能成为滑动窗口最大值的数值存入一个两端开口的队列。【以例题作为分析的例子】
4、数组的第一个数字是2,把它存入队列。第二个数字是3,由于它比前一个数字2大,因此2不可能成为滑动窗口中的最大值。先把2从队里中删除,再把3存入队列。此时队列中只剩下一个数字3.针对第三个数字4的步骤类似,最终在队列中只剩下一个数字4.此时滑动窗口已经有3个数字,而它的最大值4位于队列的头部。
5、接下来处理第4个数字2.2比队列中的数字4小。当4滑出窗口之后,2还是有可能成为滑动窗口的最大值,因此把2存入队列的尾部。现在队列中有数字4和2,其中最大值4仍然位于队列的头部
6、第五个数字6.由于它比队列中已有的两个数字4和2都打美因茨这时4和2已经不可能成为滑动窗口的最大值了。先把4和2从队列中删除,再把数字6存入队列。这时最大值6仍然位于队列的头部
7、第六个数字是2.由于它比队列中已有的数字6小,所以把2也存入队列的尾部,此时队列中有两个数字,其中最大值6仍然位于队列的头部
8、第七个数字5.在队列中已有两个数字6和2里,2小于5,因此2不可能是一个滑动窗口的最大值,可以把它从队列的尾部删除。删除数字2之后,再把5存入队列。此时队列里剩下两个数字6和5,其中位于队列头部的是最大值6
9、数组最后一个数字是1,把1存入队列的尾部。注意到位于队列头部的数字6是数组的第5个数字,此时的滑动窗口已经不再包括这个数字了,因此应该把数字6从队列中伸出,那么怎么知道滑动窗口是否包括一个数字?应该在队列里存入数字在数组里的下标,而不是数值。当一个数字的下标与当前处理的数字的下标只差大于或等于滑动窗口的大小时,这个数字已经从滑动窗口中滑出,可以从队列中删除了
10、
步骤 | 插入数字 | 滑动窗口 | 队列的下标 | 最大值 |
---|---|---|---|---|
1 | 2 | 2 | 0(2) | N/A |
2 | 3 | 2,3 | 1(3) | N/A |
3 | 4 | 2,3,4 | 2(4) | 4 |
4 | 2 | 3,4,2 | 2(4),3(2) | 4 |
5 | 6 | 4,2,6 | 4(6) | 6 |
6 | 2 | 2,6,2 | 4(6),5(2) | 6 |
7 | 5 | 6,2,5 | 4(6),6(5) | 6 |
8 | 1 | 2,5,1 | 6(5),7(1) | 5 |
std::vector<int> maxInWindows(const vector<int>& num,unsigned int size)
{
//存放结果,也就是窗口最大值的数组
vector<int> maxInWindows;
if(num.size()>size && size>=1)
{
//用来保存可能是滑动窗口最大值的数字的下标
std::deque<int> index;
//前3个数
for(unsigned int i=0;i<size;i++)
{
while(!index.empty() && num[i]>=num[index.back()])
index.pop_back();
index.push_back(i);
}
//从第三个数开始
for(unsigned int i=size;i<num.size();++i){
maxInWindows.push_back(num[index.front()]);
while(!index.empty() && num[i]>=num[index.back()])
index.pop_back();
//在窗口之外的,不可能是
if(!index.empty() && index.front() <=(int)(i-size))
index.pop_front();
index.push_back(i);
}
maxInWindows.push_back(num[index.front]);
}
return maxInWindows;
}
队列的最大值
请定义一个队列并实现函数max得到队列的最大值。要求函数max、push_back和pop_front的时间复杂度都是O(1)
思路
template<typename T> class QueueWinthMax
{
public:
QueueWinthMax():currentIndex(0){}
void push_back(T number)
{
while(!maximums.empty() && number>=maximums.back().number)
maximums.pop_back();
InternalData internalData={number,currentIndex};
data.push_back(internalData);
maximums.push_back(internalData);
++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() constexpr{
if(maximums.empty())
throw new exception("queue is empty!!");
return maximums.front().number;
}
private:
struct InternalData
{
T number;
int index;
};
deque<InternalData> data;
deque<InternalData> maximums;
int currentIndex;
}