leetcode 42接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

此方法超时

这个思路是按行求解,每次每次只加在这一行中的凹槽谁,每次加完后要对非0数据减去1,相当于水平面向上移动一格。不过这个超时的原因也很明显,如果最高的两个柱子很大的时候会造成递归很大。

public void decrea(int[] height)
    {
        for(int i=0;i<height.length;i++)
        {
            if(height[i]>0)
                height[i]-=1;
        }
    }
    boolean isCon(int[] height)
    {
        int count=0;
        for(int i=0;i<height.length;i++)
        {
            if(height[i]>0)
                count++;
        }
        if(count<=1)
            return true;
        else
            return false;
    }
    public int trap(int[] height) {
        if(isCon(height)) return 0;
        int start;
        int sum=0;
        for(int i=0;i<height.length;i++)
        {
            if(i+1<height.length&&height[i]>0&&height[i+1]==0)
            {
                start=i;
                i=i+1;
                while (i<height.length&&height[i]==0)
                    i++;
                if(i>=height.length)break;
                sum=sum+(i-start-1);
                i--;
            }
        }
//        System.out.println(sum);
        decrea(height);
        return sum+trap(height);
    }

上面方法超时

改进

https://leetcode-cn.com/problems/trapping-rain-water/solution/42-jie-yu-shui-shuang-zhi-zhen-dong-tai-wguic/
利用单调栈的思想
分为3种情况

  1. 当栈顶的元素大于当前元素的时候,此时不会构成凹槽,所以当前元素入栈
  2. 当栈顶的元素等于当前的元素的时候,此时不会形成凹槽,两种处理方法
    1. 将栈顶的元素出栈,将当前的元素送入栈中
    2. 将当前的元素入栈,这样在计算的时候会出现将当前元素作为凹槽底部,左边与它相等的元素作为左边柱子的情况,此时计算出来为0,
  3. 当前元素大小栈顶的元素的时候就会出现凹槽,此时栈顶的元素出栈作为凹槽的底部,栈顶元素作为凹槽的左侧。
 public int trap(int[] height) {
        if(height.length<3) return 0;
        LinkedList<Integer> stack= new LinkedList<>();
        stack.push(0);
        int cur=1;
        int sum=0;
        while (cur<height.length)
        {

//            System.out.println(stack.isEmpty());
            while (cur<height.length&&(stack.isEmpty()||height[cur]<height[stack.peek()]))
            {

                stack.push(cur);
                cur++;
            }
            if(cur>=height.length) break;
            while (cur<height.length&&height[cur]==height[stack.peek()])
            {
//                stack.pop();
                stack.push(cur);
                cur++;
            }
            if(cur>=height.length) break;
            while (!stack.isEmpty()&&height[stack.peek()]<height[cur])
            {

                int h=height[stack.pop()];
                if(stack.isEmpty()) break;
                int min=Math.min(height[stack.peek()],height[cur]);
                sum=sum+(min-h)*(cur-stack.peek()-1);
            }
        }
        return sum;
    }

上述代码的改进,思路一样,去掉冗余,下面这个把等于这个条件的时候没有出栈,这就会造成等于的时候计算一次结果为0然后加上。

public int trap(int[] height)
    {
        if(height.length<3) return 0;
        int sum=0;
        LinkedList<Integer> stack= new LinkedList<>();
        for(int i=0;i<height.length;i++)
        {
            while (!stack.isEmpty()&&height[i]>height[stack.peek()])
            {
                int right=height[i];
                int mid=height[stack.pop()];
                if(stack.isEmpty()) break;
                int left=height[stack.peek()];
                int h=Math.min(left,right);
                int w=i-stack.peek()-1;//注意-1
                sum=sum+(h-mid)*w;
            }
            stack.push(i);
        }
        return sum;
    }

双指针法

  1. 首先将数组的最两边捣乱的数字去掉,因为我们要用每一个竖行去接水,外层的每一次都是计算的是本竖行中所盛放的水
  2. 计算竖行的话就是要找竖行两边最高的,因为这两个点决定了这个竖行盛放的水,两个柱子中较矮的减去此竖行的高度就是这个竖行盛放的水。
 public int trap(int[] height)
    {
        if(height.length<3) return 0;
        int L_height,R_height;
        int sum=0;
       for(int i=1;i<height.length-1;i++)
       {
           L_height=height[i];
           R_height=height[i];
          for(int j=i-1;j>=0;j--)
          {
              if(L_height<height[j])
              {
                  L_height=height[j];
              }
          }
          for(int j=i+1;j<height.length;j++)
          {
              if(R_height<height[j])
              {
                  R_height=height[j];
              }
          }
         int h=Math.min(L_height,R_height)-height[i];
          if(h>0) sum=sum+h;
       }
       return sum;

    }

动态规划法

上述双指针法存在着大量的重复的比较,从双指针法中可以知道,只要知道两边最高就可以知道这个竖行盛放的水量。因此使用两个数组,左侧最高,和右侧最高来计算
leftmax=max(height[current],height[current-1])
rightmax=max(height[current],height[current+1])

public int trap(int[] height)
    {
        int[] leftMax=new int[height.length];
        int[] rightMax=new int[height.length];
        leftMax[0]=height[0];
        for(int i=1;i<height.length;i++)
        {
            leftMax[i]=Math.max(leftMax[i-1],height[i]);
        }
        rightMax[height.length-1]=height[height.length-1];
        for(int j=height.length-2;j>=0;j--)
        {
            rightMax[j]=Math.max(rightMax[j+1],height[j]);
        }
        int sum=0;
        for(int i=1;i<height.length-1;i++)
        {
            int h=Math.min(leftMax[i],rightMax[i]);
            h=h-height[i];
            sum=sum+h;
        }
        return sum;
    }
posted @ 2021-05-09 14:33  LiangLiangAA  阅读(48)  评论(0编辑  收藏  举报
theme: { name: 'geek', avatar: '', headerBackground: '' // ... },