单调栈结构
假如数组中无重复值
准备一个栈:保证栈是单调递增的,
3->1要入栈,但是不满单调递增了,没法直接入栈,栈要开始弹出,直到1可以进来
当一个位置开始从栈里弹出的时候,开始收集它的位置
比如2->7要弹出了,那么有关2位置的7,左右侧离它最近的比它小的开始生成
左侧离它最近的比它小的是1->6(它底下压的谁,谁就是左侧离它最近的比它小的)
右侧离它最近的比它小就是3->1(就是谁让它弹出的就是右侧离它最近比它小的)
依次处理
收集的答案:
2->7: 1->6,3->1
1->6: 0->5,3->1
0->5: 无,3->1
栈:
4->4
3->1
5->2要入栈,破坏了结构,没法直接入栈
收集的答案:
4->4:3->1,5->2
栈:
5->2
3->1
6->0要入栈,破坏了结构,没法直接入栈
收集的答案:
5->2:3->1,6->0
3->1:无,6->0
栈:
7->3
6->0
到这里遍历完了,遍历完了,单独把栈中剩下的位置弹出
收集的答案:
7->3:6->0,无
6->0:无,无
每个位置进栈一次,出栈一次,时间复杂度O(N)
证明:
左侧离我最近的比我小的,是根据栈的严格递增的结构决定的
右侧离我最近的比我小的,就是谁让我弹出就是答案
2、数组有重复值
入栈的不是一个值,而是一个集合
public class MonotonourStack { /** arr=[3,1,2] n 0 1 2 res:二维数组 n*2 n行,2列, 第一列代表左边最近比较小,第二列代表右边最近比较小的 [ 0:[-1,1] 1:[-1,-1] 2:[1,-1 ] */ public static int[][] getNearLess(int[] arr){ int[][] res=new int[arr.length][2]; //准备一个栈,放list是一群位置,同样值的东西,位置压在一起 //底-->顶是从小到大 Stack<List<Integer>> stack=new Stack<>(); for(int i=0;i<arr.length;i++){//i位置的arr[i]进栈 //情况一:比i大的都弹出了 //i位置的arr[i]进栈,,但是不满单调递增了(arr[stack.peek().get(0)]>arr[i]),没法直接入栈, while(!stack.isEmpty() && arr[stack.peek().get(0)]>arr[i]){ // 栈要开始弹出,直到i可以进来 List<Integer> popIs=stack.pop(); // 当一个位置开始从栈里弹出的时候,开始收集它的位置 //左侧离它最近的比它小的是:如果栈是空了那就是-1,否则就是压着的那一组中的最后一个位置 int leftIndex=stack.isEmpty()?-1:stack.peek().get(stack.peek().size()-1); //右边比它最近最小的是:i for(Integer popi:popIs){ res[popi][0]=leftIndex; res[popi][1]=i; } } //情况二:相等的 if(!stack.isEmpty() && arr[stack.peek().get(0)]==arr[i]){ stack.peek().add(i);//下标压在一起 }else{ //情况三:比i小的,重新做一个list,压入了栈中 List<Integer> list=new ArrayList<>(); list.add(i); stack.push(list); } //单独把栈中剩下的位置弹出 while (!stack.isEmpty()){ List<Integer> popIs=stack.pop(); int leftIndex=stack.isEmpty()?-1:stack.peek().get(stack.peek().size()-1); for(Integer popi:popIs){ res[popi][0]=leftIndex; res[popi][1]=-1; } } } return res; } }
应用:
有一个数组,找一个指标A,使这个指标最大
位置i ,必须以位置i作为最小值,A=子数组的累加和*arr[i]
可以转化成找离I位置左边离它最近的比它小的,和右边离它最近的比它小的,当遇到小的说明扩不动了。
/** * 有一个数组(正数),找一个指标A,使这个指标最大 * 位置i ,必须以位置i作为最小值,A=子数组的累加和*arr[i] * 可以转化成找离I位置左边离它最近的比它小的,和右边离它最近的比它小的,当遇到小的说明扩不动了。 */ private int process(int[] arr){ int[][] nearLessGrid=getNearLess(arr); int maxA=0; for(int i=0;i<nearLessGrid.length;i++){ int L=nearLessGrid[i][0]; int R=nearLessGrid[i][1]; int sum=getRangeSum(arr,L,R,i); maxA=Math.max(maxA,arr[i]*sum); } return maxA; } private int getRangeSum(int[] arr,int L,int R,int i){ //i=0 -1,1 range i..1 //i=2 1,-1 range 1..i //i=3 1,5 range 1..3..5 if(L==-1){ L=i; } if(R==-1){ R=i; } int sum=0; for(int j=L;j<=R;j++){ sum+=arr[i]; } return sum; }
739. 每日温度
请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表
temperatures = [73, 74, 75, 71, 69, 72, 76, 73],
你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
class Solution { public int[] dailyTemperatures(int[] temperatures) { return process(temperatures); } private int[] process(int[] temperatures){ //stack<List<Integer>> big...small //when value stack.peek <i then i-stack.peek //when value == stack.peek.add(i) //when value > i stack.push(List{i}) //when stack is not empty 0 int[] res=new int[temperatures.length]; Stack<List<Integer>> stack=new Stack<>(); for(int i=0;i<temperatures.length;i++){ //pop small while(!stack.isEmpty() && temperatures[stack.peek().get(0)]<temperatures[i]){ List<Integer> popList=stack.pop(); for(Integer popi:popList){ res[popi]=i-popi; } } if(!stack.isEmpty() && temperatures[stack.peek().get(0)] == temperatures[i]){ stack.peek().add(i); }else{ List<Integer> list=new ArrayList<>(); list.add(i); stack.push(list); } } return res; } }
496.下一个更大元素 I
给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。
请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。
nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。
示例 1:
输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
对于 num1 中的数字 4 ,你无法在第二个数组中找到下一个更大的数字,因此输出 -1 。
对于 num1 中的数字 1 ,第二个数组中数字1右边的下一个较大数字是 3 。
对于 num1 中的数字 2 ,第二个数组中没有下一个更大的数字,因此输出 -1 。
class Solution { public int[] nextGreaterElement(int[] nums1, int[] nums2) { return process(nums1,nums2); } private int[] process(int[] nums1,int[] nums2){ //stack bigger...small //1:找到nums2中每个位置离它最近比它大的位置[1,2,-1,-1] 0 1 2 3 nums2 res //2:找到nums1中的出现的数在nums2中的位置i, //3:对应到res[i] -1->-1,如果不是-1,那么找到nums2[res[i]] Stack<Integer> stack=new Stack<>(); HashMap<Integer,Integer> map=new HashMap<>(); int[] res=new int[nums2.length]; for(int i=0;i<nums2.length;i++){ map.put(nums2[i],i); while(!stack.isEmpty() && nums2[stack.peek()]<nums2[i]){ int pop=stack.pop(); res[pop]=i; } stack.push(i); } while(!stack.isEmpty()){ res[stack.pop()]=-1; } int[] ans=new int[nums1.length]; for(int i=0;i<nums1.length;i++){ int index=map.get(nums1[i]); if(res[index]==-1){ ans[i]=-1; }else{ ans[i]=nums2[res[index]]; } } return ans; } }
503.下一个更大元素II
给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。
示例 1:
- 输入: [1,2,1]
- 输出: [2,-1,2]
- 解释: 第一个 1 的下一个更大的数是 2;数字 2 找不到下一个更大的数;第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
思路
在遍历的过程中模拟走了两边nums
class Solution { public int[] nextGreaterElements(int[] nums) { return process(nums); } private int[] process(int[] nums){ //stack bigger....small --> if i is the bigger then res that the first bigger to pop //for i,0....2*nums.length 0 1 2 3 4 5 //get value i%nums.length Stack<Integer> stack=new Stack<>(); int[] res=new int[nums.length]; Arrays.fill(res,-1); for(int i=0;i<2*nums.length;i++){ while(!stack.isEmpty() && nums[stack.peek()]<nums[i%nums.length]){ res[stack.pop()]=nums[i%nums.length]; } stack.add(i%nums.length);//1->2,1->2,2->1 } return res; } }