双指针(1)

179 阅读1分钟

Day02 双指针技巧-接雨水、盛水最多的容器

1、盛水最多的容器

  • 思路

    • 题意分析:盛水容器的体积公式:((MIN[height[l],height[r]])(rl)) * (r - l)
    • 双指针:
      • height 数组的左右端点 l、r ,计算并保存此时的容积
      • 由上述公式,应该移动 MIN[height[l],height[r]]MIN[height[l],height[r]] 结果(高度小的下标移动)对应的 l、r 下标

      注:容积的大小由 下标区间(r-l) 和 **最矮高度MIN(height[l],height[r])MIN(height[l],height[r])**决定。移动矮的一方,容积的面积有变大的可能。反之,则容积的面积不会变大。

  • 题解

    class Solution {
        public int maxArea(int[] height) {
            int left = 0, right = height.length - 1;
            int res = 0;
            while (left < right) {
                // [left, right] 之间的矩形面积
                int cur_area = Math.min(height[left], height[right]) * (right - left);
                res = Math.max(res, cur_area);
                // 双指针技巧,移动较低的一边
                if (height[left] < height[right]) {
                    left++;
                } else {
                    right--;
                }
            }
            return res;
        }
    }
    

2、接雨水

  • 思路
    • 局部分析,当每个位置i都接到最多的雨水时,每个位置的和即为问题的解
      • ans=ans[0]+...+ans[i]ans = ans[0] + ... + ans[i]
      • ans[i]=ans[i] = |MIN[lmax,rmax]MIN[l_max,r_max] - height[i]|
    • 题解
    class Solution {
        public int trap(int[] height) {
            int ans = 0;
            for (int i = 0; i < height.length; i++) {
                int l_max = 0;
                int r_max = 0;
                for (int j = 0; j < i; j++) {
                    l_max = Math.max(l_max, height[j]);
                }
                for (int j = height.length - 1; j > i; j--) {
                    r_max = Math.max(r_max, height[j]);
                }
                int max = Math.min(r_max, l_max) - height[i];
                ans += Math.max(max, 0);
            }
            return ans;
        }
    }
    
  • 优化
    • l_max和r_max每次都需要在循环中遍历,进行优化
      • l_max[] r_max[] 数组进行打表,在循环开始前先进行两次循环
      class Solution {
          public int trap(int[] height) {
              int ans = 0;
              int len = height.length;
              //l_max[i]表示i位置左侧的最大值
              int[] l_max = new int[len];
              //r_max[i]表示i位置右侧的最大值
              int[] r_max = new int[len];
              l_max[0] = height[0];
              l_max[len - 1] = height[len - 1];
              for (int i = 1; i < len; i++) {
                  l_max[i] = Math.max(height[i],l_max[i - 1]);
              }
              for (int i = len - 2; i >= 0; i--) {
                  r_max[i] = Math.max(height[i],r_max[i + 1]);
              }
              for (int i = 1; i < len - 1; i++) {
                  ans += Math.min(r_max[i], l_max[i]) - height[i];
              }
              return ans;
          }
      }
      
    • 双指针解法-两端同时接水
      • 有i,j位置同时接水。此时有iLeftMAX,iRightMAX和jLeftMAX,jRightMAX
      • 双指针,i,j分别为左右指针,即i < j
      • iLeftMAX<=jLeftMAX jRightMAX<=iRightMAX (单调栈)。实际双指针只有iLeftMAX和jRightMAX。(l_max和r_max)
      • 如果 iLeftMAX > jRightMAX (l_max > r_max) 则 jLeftMAX > jRightMAX。j处雨水应该是 jRightMAX - height[j],i处无法推断)

      • 如果 jRightMAX > iLeftMAX (r_max > l_max) 则 iRightMAX > iLeftMAX。i处雨水应该是 iLeftMAX - height[i], j处无法推断)

      class Solution {
         public int trap(int[] height) {
             int ans = 0;
             int len = height.length;
             int l_max = 0;
             int r_max = 0;
             int l = 0, r= len - 1;
             while (l <= r){
                 l_max = Math.max(l_max,height[l]);
                 r_max = Math.max(r_max,height[r]);
                 if (l_max > r_max){
                     ans += r_max - height[r];
                     r --;
                 }else {
                     ans += l_max - height[l];
                     l ++;
                 }
             }
             return ans;
         }
      }