双指针l, r从两边向中间逼近

lMaxH记录l左侧最高的柱子高度
rMaxH记录r右侧最高的柱子高度

核心思想是:
 - 如果lH < rH
   那么所有l左侧的柱子也 < rH (i.e. lMax < rH).
   如果同时lH < lMaxH, 则l的盛水量为lMaxH-lH 
     i.e.l被lMax和r两高柱相夹, lMax为短板.
     
时间 O(n), 空间 O(1)
import java.util.*;

public class Solution {
    public long maxWater (int[] arr) {
      if (arr.length <= 2) return 0l;
      
      int l = 0, r = arr.length-1;
      int lMaxH = 0, rMaxH = 0;
      long water = 0;
      while (l < r) {
        int lH = arr[l], rH = arr[r];
        if (lH <= rH) {
          water += Math.max(0, lMaxH - lH);
          lMaxH = Math.max(lH, lMaxH);
          l++;
        } else {
          water += Math.max(0, rMaxH - rH);
          rMaxH = Math.max(rH, rMaxH);
          r--;
        }
      }
      return water;
    }
}
之前写的奇奇怪怪的双指针算面积。留着自己复习看。

preMaxIdx指向之前最高,i指向当前柱子
如果i比preMaxIdx高, 加水。
加水量 = (i与preMaxIdx相夹的空间) - (i与preMaxIdx相夹的柱子总高)
从左向右走一遍(preMaxIdx在左侧),从右向左再走一遍(preMaxIdx在右侧)
i与preMaxIdx等高只在从左向右时计算(避免double-counting)。
时间 O(n), 空间 O(1)
import java.util.*;

public class Solution {
  static long blocks = 0;  // 当前柱子和前高之间的方块数量
  static long water = 0;   // 已存水的总量
  static int preMaxIdx = 0;  // 前高的下标
  
  public long maxWater (int[] arr) {
    if (arr.length <= 2) return 0;
    // 从左向右找更高
    for (int i = 1; i < arr.length; i++) update(i, arr);
    // 从右向左找更高
    preMaxIdx = arr.length-1; blocks = 0;
    for (int i = arr.length-2; i >= 0; i--) update(i, arr);

    return water;
  }
  
  void update(int curIdx, int[] arr) {
    long curH = arr[curIdx];
    long preMaxH = arr[preMaxIdx];
    
    if (curH > preMaxH || 
        // 两柱等高只计算一遍 (i.e. 从左向右时计算)
        (curH == preMaxH && preMaxIdx < curIdx)) {
      // 如果当前柱子高于之前最高,盛水量 = 两柱相夹的空间 - blocks
      water += (Math.abs(preMaxIdx - curIdx) - 1) * preMaxH - blocks;
      preMaxIdx = curIdx;
      blocks = 0;
    } else {
      blocks += curH;
    }
  }
}