算法原理:
用单调递增栈,当该元素可以入栈的时候,栈顶元素就是它左侧第一个比它小的元素。
以:3 4 2 7 5 为例,过程如下:(小到大)
#include<iostream>
using namespace std;
const int N=1e6+5;
int stk[N],tt;
int main() {
int n;
cin>>n;
while(n--) {
int x;
scanf("%d",&x);
while(tt&&stk[tt]>=x)tt--;//如果栈顶元素大于等于当前待插入的值,就出栈
if(!tt) {
//说明左边没有比他大的
cout<<-1<<" ";
} else {
cout<<stk[tt]<<" ";
}
stk[++tt]=x;
}
return 0;
}
解题思路(以最大值为例):
由于我们需要求出的是滑动窗口的最大值。
如果当前的滑动窗口中有两个下标 i 和 j ,其中i在j的左侧(i<j),并且i对应的元素不大于j对应的元素(nums[i]≤nums[j]),则:
当滑动窗口向右移动时,只要 i 还在窗口中,那么 j 一定也还在窗口中。这是由于 i 在 j 的左侧所保证的。
因此,由于 nums[j] 的存在,nums[i] 一定不会是滑动窗口中的最大值了,我们可以将nums[i]永久地移除。
因此我们可以使用一个队列存储所有还没有被移除的下标。在队列中,这些下标按照从小到大的顺序被存储,并且它们在数组nums中对应的值是严格单调递减的。
当滑动窗口向右移动时,我们需要把一个新的元素放入队列中。
为了保持队列的性质,我们会不断地将新的元素与队尾的元素相比较,如果新元素大于等于队尾元素,那么队尾的元素就可以被永久地移除,我们将其弹出队列。我们需要不断地进行此项操作,直到队列为空或者新的元素小于队尾的元素。
由于队列中下标对应的元素是严格单调递减的,因此此时队首下标对应的元素就是滑动窗口中的最大值。
窗口向右移动的时候。因此我们还需要不断从队首弹出元素保证队列中的所有元素都是窗口中的,因此当队头元素在窗口的左边的时候,弹出队头。
#include<iostream> #include<deque> #include<algorithm> using namespace std; const int N=1e6+5; int a[N],q[N],hh,tt=-1; int main() { int n,k; cin>>n>>k; for(int i=1; i<=n; i++) { scanf("%d",&a[i]); } deque<int>q; for(int i=1; i<=n; i++) { while(q.size()&&q.back()>a[i]) { //新进入窗口的值小于队尾元素 q.pop_back(); } q.push_back(a[i]);//将新元素入队 if(i-k>=1&&q.front()==a[i-k]) { //判断队头是否滑出了窗口 //队头出列 q.pop_front(); } if(i>=k)//窗口i形成了,输出队头对应的值 cout<<q.front()<<" "; } q.clear();//清空队列 cout<<endl; for(int i = 1; i <= n; i++) { while(q.size() && q.back() < a[i]) q.pop_back();//新进入的元素大于队尾元素 q.push_back(a[i]); if(i - k >= 1 && a[i - k] == q.front()) q.pop_front();//判断队头是否出列 if(i >= k) cout << q.front() << " "; } return 0; }