单调栈
解决问题:一个数组中每一个数,得到左边最近的比它大的和右边最近的比它大的数(小的话同理),代价尽量低
经典方法:来到每一个数左边遍历,右边遍历,O(N^2)
单调栈流程(求大的)
准备一个栈:要求栈的规则是从栈底到栈头是大到小
如果数组中的数加入栈的时候能满足规则就直接加入,不满足规则时,每弹出一个数就可以收集信息了,弹出的数的左边最近的比它大的数就是栈中该数的下一个,右边最近的比它大的数就是准备加入栈的数(谁让你弹出的数)
数组遍历完时如果栈中还有数据,就进入清算阶段,依次弹出栈中的数并收集信息,此时栈中的每一条数据都没有右边最近的比它大的数据
时间复杂度:每个数据都只进栈一次,出栈一次,所以为O(N)
public int[][] getNearBiggerNoRepeat(int[] arr){
int[][] res=new int[arr.length][2];
Stack<Integer> stack=new Stack<>();
for(int i=0;i<arr.length;i++){
while(!stack.isEmpty()&&arr[stack.peek()]<arr[i]){
int popIndex=stack.pop();
int leftBiggerIndex=stack.isEmpty()?-1:stack.peek();
res[popIndex][0]=leftLessIndex;
res[popIndex][1]=i;
}
stack.push(i);//添加操作不要忘记了
}
while(!stack.isEmpty()){
int popIndex=stack.pop();
int leftBiggerIndex=stack.isEmpty()?-1:stack.peek();
res[popIndex][0]=leftLessIndex;
res[popIndex][1]=-1;
}
return res;
}
以上是针对没有重复值的数组
如果是有重复值的数组:
栈中存链表,链表中存索引,过程类似,收集信息的时候收集的是链表中最后一个索引,两个数相等时索引存在链表后面
public int[][] getNearBigger(int[] arr){
int[][] res=new int[arr.length][2];
Stack<ArrayList<Integer>> stack=new Stack<>();
for(int i=0;i<arr.length;i++){
while(!stack.isEmpty()&&arr[stack.peek().get(0)]<arr[i]){
ArrayList<Integer> popList=stack.pop();
int leftBiggerIndex=stack.isEmpty()?-1:stack.peek().get(stack.peek().size()-1);
for(Integer num:popList){
res[num][0]=leftBiggerIndex;
res[num][1]=i;
}
}
//添加操作
if(!stack.isEmpty()&&arr[stack.peek().get(0)]==arr[i]){
stack.peek().add(Integer.valueOf(i));
}else{
ArrayList<Integer> list=new ArrayList<>();
list.add(i);
stack.push(list);
}
}
while(!stack.isEmpty()){
ArrayList<Integer> list=stack.pop();
int leftBiggerIndex=stack.isEmpty()?-1:stack.peek().get(stack.peek().size()-1);
for(Integer num:list){
res[num][0]=leftBiggerIndex;
res[num][1]=-1;
}
}
return res;
}
题目
要求:数组中的数都是正数
大流程:在每个数都是自己子数组的最小值的前提下,哪个子数组能求得最大累加和,答案一定在其中
单调栈(小到大)
每个数的左边最近的比它小的数是不能扩到的位置,右边最近的比它小的数是不能扩到的位置,通过这两个信息就可以得到每个数能扩的范围
public int getMax(int[] arr){
int size=arr.length;
int[] sums=new int[size];
sums[0]=arr[0];
for(int i=1;i<size;i++){
sums[i]=sums[i-1]+arr[i];//sums数组中存的是每个数之前的数的累加和
}
int max=Integer.MIN_VALUE;
Stack<Integer> stack=new Stack<>();
for(int i=0;i<size;i++){
while(!stack.isEmpty()&&arr[stack.peek()]>=arr[i]){//小到大规则(相等的时候也进行操作)
int j=stack.pop();
max=Math.max(max,(stack.isEmpty()?sums[i-1]:(sums[i-1]-sums[stack.peek()]))*arr[j]);//如果栈是空的说明左边没有比它小的数,而此时进来的数比它小,所以该数最小的子数组的值即为sums数组中[i-1]位置上对应的值*arr[j],否则使用(sums[i-1]-sums[stack.peek()])*arr[j]
}
stack.push(i);
}
while(!stack.isEmpty()){
int j=stack.pop();
max=Math.max(max,(stack.isEmpty()?sums[size-1]:(sums[size-1]-sums[stack.peek()]))*arr[j]);//此时在栈中的数右边都没有比它小的数,如果栈为空说明它是数组中最小的数;栈不为空说明左边有比它小的数
}
return max;
}