0
点赞
收藏
分享

微信扫一扫

【210402】面试题 17.21. 直方图的水量

斗米 2022-03-11 阅读 29

问题转化

最小子问题:每根柱子的水量由其左边最高的柱子其右边最高的柱子决定
短板效应:对左右的最高柱子取其较小值,与当前柱子比较高度

数组任一边界当前柱子围成的区间中的最大值

法一:动态规划

动态规划:解决重复子问题

  • leftMax[i]:前 i 个元素中,最大的值
  • rightMax[i]:后 i 个元素中,最大的值
//官方题解
class Solution {
    public int trap(int[] height) {
        int n = height.length;
        if (n == 0) {
            return 0;
        }

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

        int[] rightMax = new int[n];
        rightMax[n - 1] = height[n - 1];
        for (int i = n - 2; i >= 0; --i) {
            rightMax[i] = Math.max(rightMax[i + 1], height[i]);
        }

        int ans = 0;
        for (int i = 0; i < n; ++i) {
            ans += Math.min(leftMax[i], rightMax[i]) - height[i];
        }
        return ans;
    }
}

法二:双指针

注意到,法一状态转移时,dp[ i ] 只依赖于 dp[ i-1 ],因此我们可以使用变量来代替 dp 数组。

//官方题解
class Solution {
    public int trap(int[] height) {
        int ans = 0;
        int left = 0, right = height.length - 1;
        int leftMax = 0, rightMax = 0;
        while (left < right) {
            leftMax = Math.max(leftMax, height[left]);
            rightMax = Math.max(rightMax, height[right]);
            if (height[left] < height[right]) {
                ans += leftMax - height[left];
                ++left;
            } else {
                ans += rightMax - height[right];
                --right;
            }
        }
        return ans;
    }
}

法三:单调栈(分层)

  • 枚举每根柱子
  • 维持一个单调非增的栈
  • <= 栈顶:入栈
  • > 栈顶:
    • 小于 height[ i ] 的全都出栈,最后 i 入栈

    • 分层:每出栈一个 top,填补【左:left】和【右:i】之间的面积,看作先处理min(left,i)top之间层的面积。宽度:i - left;高度:min(left,i) - top

class Solution {
    public int trap(int[] height) {
        int ans = 0;
        Deque<Integer> stack = new LinkedList<Integer>();
        int n = height.length;
        for (int i = 0; i < n; ++i) {
            while (!stack.isEmpty() && height[i] > height[stack.peek()]) {
                int top = stack.pop();
                if (stack.isEmpty()) {
                    break;
                }
                int left = stack.peek();
                int currWidth = i - left - 1;
                int currHeight = Math.min(height[left], height[i]) - height[top];
                ans += currWidth * currHeight;
            }
            stack.push(i);
        }
        return ans;
    }
}

法四:双指针(分层)

  • 指针从两边向中间移动,遇到next柱高>当前层high停下,当前层遍历结束
  • 计算面积:【宽:right - left +1】*【高:high=1】
  • 边界条件:left <= right
public class Solution {
     public int trap(int[] height)
     {
         int sum = 0;
         for(int i = 0;i<height.length;i++) {
            sum += height[i];
         }//求数组总和
         int volume = 0; // 总体积和高度初始化
         int high = 1;
         int size = height.length;
         int left = 0; // 双指针初始化
         int right = size - 1;
         while (left <= right) {
             while (left <= right && height[left] < high) {
                 left++;
             }
             while (left <= right && height[right] < high) {
                 right--;
             }
             volume += right - left + 1; // 每一层的容量都加起来
             high++; // 高度加一
         }
         return volume -sum; // 总体积减去柱子体积,即雨水总量
     }
}
举报

相关推荐

0 条评论