目录
单调栈模板
1、模拟栈
单调队列模板
1、模拟队列
2、双端队列
135. 最大子序和 - 前缀和+滑动窗口+单调队列
单调栈模板
什么时候用单调栈?
求序列中每一个数左边或右边第一个比它大或小的数
1、单调递增栈
在保持栈内元素单调递增前提下(如果栈顶元素大于待入栈元素,弹出栈顶),新元素入栈
对于要入栈的元素,在对栈进行更新后,栈顶元素就是数组中左侧第一个比自己小的元素
2、单调递减栈
在保持栈内元素单调递减前提下(如果栈顶元素小于要待入栈元素,弹出栈顶),新元素入栈
对于要入栈的元素,在对栈进行更新后,栈顶元素就是数组中左侧第一个比自己大的元素
活动 - AcWing
题目:输出每个数左边第一个比自己小的数,如果不存在则输出-1
1、模拟栈
//进栈 stk[tt++]=x; //出栈 tt--; //输出栈顶元素 stk[tt-1]
//模拟栈
import java.util.*;
class Main
{
static int N=100010;
static int[] stk=new int[N];
static int tt;
public static void main(String[] args)
{
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
while(n-->0)
{
int x=sc.nextInt();
while(tt>0&&stk[tt]>=x) tt--;
if(tt==0) System.out.print("-1 ");
else System.out.print(stk[tt]+" ");
stk[++tt]=x;
}
}
}
import java.util.*;
class Main
{
public static void main(String[] args)
{
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
Deque<Integer> stk=new LinkedList<>();
while(n-->0)
{
int x=sc.nextInt();
while(!stk.isEmpty()&&stk.peek()>=x) stk.pop();
if(stk.isEmpty()) System.out.print("-1 ");
else System.out.print(stk.peek()+" ");
stk.push(x);
}
}
}
单调队列模板
活动 - AcWing
题目:
思路:
先求最小值,维护一个单调递增的队列存下标,队头永远是最小的
如果新入队的元素≤队尾,则去掉队尾(因为只要最小的,大的没用)
如果最小值在合法的窗口内,则输出它
再求最大值,维护一个单调递减的队列存下标,队头永远是最大的
如果新入队的元素≥队尾,则去掉队尾(因为只要最大的,小的没用)
如果最大值在合法窗口内,输出
1、模拟队列
int q[N]; int hh=0,tt=-1; //入队 q[++hh]=x; //出队 hh++; //取队头元素 q[hh];
import java.util.*;
import java.io.*;
import java.math.*;
class Main
{
static int N=1000010;
static int[] a=new int[N],q=new int[N];
//队头q[hh]存最大/最小值的下标 队尾q[tt]更新为当前元素的下标
public static void main(String[] args) throws IOException
{
PrintWriter wt=new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
int n=rd.nextInt(),k=rd.nextInt();
for(int i=0;i<n;i++) a[i]=rd.nextInt();
int hh=0,tt=-1;
//求窗口内最小值
for(int i=0;i<n;i++)
{
if(hh<=tt&&i+1-k>q[hh]) hh++; //保证区间左侧下标合法
while(hh<=tt&&a[q[tt]]>=a[i]) tt--; //如果队尾元素≥a[i]则删掉队尾 因为要最小的 比它大的没有用
q[++tt]=i;
if(i+1-k>=0) wt.print(a[q[hh]]+" "); //在合法窗口内
}
wt.println();
hh=0;tt=-1;
//求窗口内最大值
for(int i=0;i<n;i++)
{
if(hh<=tt&&i+1-k>q[hh]) hh++; //保证区间左侧下标合法
while(hh<=tt&&a[q[tt]]<=a[i]) tt--; //如果队尾元素≤a[i]则删掉队尾 因为要最大的 比它小的没用
q[++tt]=i;
if(i+1-k>=0) wt.print(a[q[hh]]+" ");
}
wt.flush();
}
static class rd
{
static BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
static StringTokenizer tk=new StringTokenizer("");
static String nextLine() throws IOException
{
return bf.readLine();
}
static String next() throws IOException
{
while(!tk.hasMoreTokens()) tk=new StringTokenizer(bf.readLine());
return tk.nextToken();
}
static int nextInt() throws IOException
{
return Integer.parseInt(next());
}
static double nextDouble() throws IOException
{
return Double.parseDouble(next());
}
static long nextLong() throws IOException
{
return Long.parseLong(next());
}
static BigInteger nextBig() throws IOException
{
BigInteger d=new BigInteger(rd.nextLine());
return d;
}
}
}
2、双端队列
import java.util.*;
import java.io.*;
import java.math.*;
class Main
{
static int N=1000010;
static int[] a=new int[N];
//队头q[hh]存最大/最小值的下标 队尾q[tt]更新为当前元素的下标
public static void main(String[] args) throws IOException
{
PrintWriter wt=new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
int n=rd.nextInt(),k=rd.nextInt();
for(int i=0;i<n;i++) a[i]=rd.nextInt();
Deque<Integer> q1=new ArrayDeque<>();
//求窗口内最小值
for(int i=0;i<n;i++)
{
if(!q1.isEmpty()&&i+1-k>q1.peekFirst()) q1.pollFirst(); //保证区间左侧下标合法
while(!q1.isEmpty()&&a[q1.peekLast()]>=a[i]) q1.pollLast(); //如果队尾元素≥a[i]则删掉队尾 因为要最小的 比它大的没有用
q1.offerLast(i);
if(i+1-k>=0) wt.print(a[q1.peekFirst()]+" "); //在合法窗口内
}
wt.println();
Deque<Integer> q2=new ArrayDeque<>();
//求窗口内最大值
for(int i=0;i<n;i++)
{
if(!q2.isEmpty()&&i+1-k>q2.peekFirst()) q2.pollFirst(); //保证区间左侧下标合法
while(!q2.isEmpty()&&a[q2.peekLast()]<=a[i]) q2.pollLast(); //如果队尾元素≤a[i]则删掉队尾 因为要最大的 比它小的没用
q2.offerLast(i);
if(i+1-k>=0) wt.print(a[q2.peekFirst()]+" ");
}
wt.flush();
}
static class rd
{
static BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
static StringTokenizer tk=new StringTokenizer("");
static String nextLine() throws IOException
{
return bf.readLine();
}
static String next() throws IOException
{
while(!tk.hasMoreTokens()) tk=new StringTokenizer(bf.readLine());
return tk.nextToken();
}
static int nextInt() throws IOException
{
return Integer.parseInt(next());
}
static double nextDouble() throws IOException
{
return Double.parseDouble(next());
}
static long nextLong() throws IOException
{
return Long.parseLong(next());
}
static BigInteger nextBig() throws IOException
{
BigInteger d=new BigInteger(rd.nextLine());
return d;
}
}
}
135. 最大子序和 - 前缀和+滑动窗口+单调队列
活动 - AcWing
题目:
输入一个长度为 n 的整数序列,从中找出一段长度不超过 m 的连续子序列,使得子序列中所有数的和最大
注意: 子序列的长度至少是 1
思路:
- 首先求一段连续数的和,用前缀和
- 枚举区间的右端点i,当i固定时,问题转化成:寻找一个j,使 j [i-m,i-1],且s[j]最小
- 一段区间的和,可以转化为前缀和相减的形式,前缀和减去的数越小,则区间和越大
- 对于任意的j和k,满足k<j<i,如果s[k]>=s[j],对于所有≥i的右端点,k永远不会成为最佳选择,因为不仅s[k]更大,而且k<j,长度容易超出m,因此去掉k
- 可以维护一个单调递增的队列,队头即为最小
/*
*道阻且长,行则将至*
author:Roye_ack
*/
import java.util.*;
import java.io.*;
import java.math.*;
class Main
{
static int N=300010;
static int[] s=new int[N];
public static void main(String[] args) throws IOException
{
PrintWriter wt=new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
int n=rd.nextInt(),m=rd.nextInt();
for(int i=1;i<=n;i++)
{
s[i]=rd.nextInt();
s[i]+=s[i-1];
}
Deque<Integer> q=new ArrayDeque<>();
q.offerLast(0); //s[i]-s[0]就是s[i]
int res=-0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
if(!q.isEmpty()&&i-q.peekFirst()>m) q.pollFirst(); //如果长度超过m则缩小滑窗
res=Math.max(res,s[i]-s[q.peekFirst()]);
while(!q.isEmpty()&&s[q.peekLast()]>=s[i]) q.pollLast(); //维护单调递增的队列
q.offerLast(i);
}
wt.print(res);
wt.flush();
}
static class rd
{
static BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
static StringTokenizer tk=new StringTokenizer("");
static String nextLine() throws IOException
{
return bf.readLine();
}
static String next() throws IOException
{
while(!tk.hasMoreTokens()) tk=new StringTokenizer(bf.readLine());
return tk.nextToken();
}
static int nextInt() throws IOException
{
return Integer.parseInt(next());
}
static double nextDouble() throws IOException
{
return Double.parseDouble(next());
}
static long nextLong() throws IOException
{
return Long.parseLong(next());
}
static BigInteger nextBig() throws IOException
{
BigInteger d=new BigInteger(rd.nextLine());
return d;
}
}
}