单调栈结构



 

 

 假如数组中无重复值

准备一个栈:保证栈是单调递增的,

 

 

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;

        
    }
}

  

 

posted @ 2021-09-05 16:03  sherry001  阅读(69)  评论(0编辑  收藏  举报