Day51 算法记录| 动态规划 18(单调栈)

单调栈

  • 739. 每日温度
  • 496.下一个更大元素 I
  • 503. 下一个更大元素 II
  • 42. 接雨水
  • 84. 柱状图中最大的矩形

单调栈:找最近的比他大的值
最近大的值:需要一个单调递减的栈(大于栈顶元素就弹出)
最近最小值:单调递减栈

方向: 右边,从后往前遍历 左边:从头遍历

739. 每日温度

单调栈:找最近的比他大的值
我最开始也是两层for循环,严重超时
通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。时间复杂度为O(n)。

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
     int n = temperatures.length;
     int[] res = new int[n];
     Deque<Integer> stack = new LinkedList<>();

     for(int i=0;i<n;i++){
         while(!stack.isEmpty()&&temperatures[i]>temperatures[stack.peek()]){
             int index = stack.pop();
             res[index] = i - index;
         }
         stack.push(i);
     }
     return res;
    }
}

496.下一个更大元素 I

我自己的思路:
直接用上面的方法,求出原有的nums2 [ ] 对应的res2[ ],里面存放的是下一个大的值
然后两层for循环遍历,求出nums1[ ]对应的res1[ ]

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int n = nums1.length;
      int[] res1 = new int[n];
      int[] res2 = next(nums2);
      for(int i=0;i<n;i++){
          for(int j=0;j<nums2.length;j++){
              if(nums1[i] == nums2[j]){
                  res1[i] = res2[j];
              }
          }
      }
      return res1;
    }

    private int[] next(int[] nums2){
     int n = nums2.length;
     int[] res = new int[n];
     Arrays.fill(res,-1);
     Deque<Integer> stack = new LinkedList<>();

     for(int i=0;i<n;i++){
         while(!stack.isEmpty()&&nums2[i]>nums2[stack.peek()]){
             int index = stack.pop();
             res[index] =nums2[i];
         }
         stack.push(i);
     }
     return res;
    }
    
}

单调栈和哈希表

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
    Deque<Integer> stack = new LinkedList<>();
    HashMap<Integer,Integer> map = new HashMap<>();// 键放nums2的元素,值放比它大的最近的元素

    for(int num :nums2){
        while(!stack.isEmpty()&& num>stack.peek()){
            map.put(stack.pop(), num);
        }
        stack.push (num);
    }

    int[] res = new int[nums1.length];
    for(int i=0;i<nums1.length;i++){
       res[i] = map.getOrDefault(nums1[i],-1);
       
    }
  return res;
    }
}

503. 下一个更大元素 II

和739一模一样,只是多了扩大数组这一步

Day51 算法记录| 动态规划 18(单调栈)_第1张图片

class Solution {
    public int[] nextGreaterElements(int[] nums) {
   Deque<Integer> stack = new LinkedList<>();
   int n = nums.length;

   int[] newArr = Arrays.copyOf(nums, 2*n); //newArr = [nums,nums] 扩展成两倍
    System.arraycopy(nums, 0, newArr, n, n);

   int[] res = new int[2*n]; //右边最近的大值
    Arrays.fill(res,-1);

   for(int i=0;i<2*n;i++){
       while(!stack.isEmpty()&&newArr[stack.peek()]<newArr[i]){
           res[stack.pop()] = newArr[i];
       }
       stack.push(i);
   }
   
   int[] newRes =  Arrays.copyOf(res, n); // 只需要前n个数据
 
  return newRes;
    }
}

不需要扩展了,想要遍历前面的数据就用:i%n来表示循环,i<2n,循环两次

class Solution {
    public int[] nextGreaterElements(int[] nums) {
   Deque<Integer> stack = new LinkedList<>();
   int n = nums.length;
   int[] res = new int[n];
   Arrays.fill(res,-1);
   
   for(int i=0;i<2*n;i++){
       int num = nums[i%n];
     while(!stack.isEmpty()&&nums[stack.peek()]< num){
       res[stack.pop()] = num;
     }
     if(i<n){
         stack.push(i);
     }
   }
   return res;
    }
}

42. 接雨水

两种方法都讲解的超级好呀!!!

方法一:
当前位置能接到的水 = MIN(前面的最大高度, 后面的最大高度)- 当前的高度

class Solution {
    public int trap(int[] height) {

        int n = height.length;
        int[] pre = new int[n]; //
        int[] next = new int[n];
        
       pre[0] = height[0];
        for(int i=1;i<n;i++){
            pre[i] = Math.max(pre[i-1],height[i]);
        }

        next[n-1] = height[n-1];
        for(int j=n-2;j>=0;j--){
            next[j]=Math.max(next[j+1],height[j]);
        }
       
       int ans =0;
       for(int i=0;i<n;i++){
          ans +=Math.min(pre[i],next[i]) - height[i];
       }
        
   return ans;
    }
}

思路二:
如果前缀最大值 < 后缀最大值, 左边 l e f t left left木桶的容量就是 前缀最大值 - h e i g h t [ i ] height[i] height[i]
如果前缀最大值 > 后缀最大值, 右边 r i g h t right right木桶的容量就是 后缀最大值 - h e i g h t [ i ] height[i] height[i]

class Solution {
    public int trap(int[] height) {
    int left =0;
    int right = height.length-1;
    int ans = 0;
    int pre = 0;
    int suf =0;

    while(left<=right){
       pre = Math.max(height[left],pre);
       suf = Math.max(height[right],suf);

       if(pre<suf){
           ans += pre - height[left];
           left++;
       }else{
           ans += suf-height[right];
           right--;
       }
    
    }
       
   return ans;
    }
}

方法三:单调栈

找左右比自己大的元素, 当前位置的面积= (矮柱子-当前高度)*两柱子之间的宽度

class Solution {
    public int trap(int[] height) {
    Deque<Integer> stack = new LinkedList<>();
    int ans =0;

    for(int i=0;i<height.length;i++){
        while(!stack.isEmpty()&& height[i] > height[stack.peek()]){
            int cur = height[stack.pop()];
            if(!stack.isEmpty()){
            int h = Math.min(height[stack.peek()],height[i]);
            ans += (h-cur)*(i-stack.peek()-1); //是两柱子之间的高度,所以-1
            }
        }
        stack.push(i);
    }
   return ans;
    }
}

84. 柱状图中最大的矩形

和上一题思路类似,找到左右两边比自己小的元素
比自己小的:所以需要单调递增的栈
每个点对应的最大面积 m a x max max =( 右边最近小 - 左边最近小 -1) * 当前高度

class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
     int[] newH = new int[n+2];
     System.arraycopy(heights,0,newH,1,n);
     int ans =0;

     Deque<Integer> stack = new LinkedList<>();
     for(int i=0;i<n+2;i++){
         
         while(!stack.isEmpty() && newH[i]<newH[stack.peek()]){
             int cur = newH[stack.pop()];
             if(!stack.isEmpty()){
                 int l = stack.peek(); //左边比它低的
                 ans = Math.max(ans,cur*(i-l-1));
             }
         }
         stack.push(i);
     }

     return ans;
    }
}

你可能感兴趣的:(算法,动态规划,java)