LeetCode 面试题17.21 直方图的水量

题目链接:LeetCode 面试题17.21 直方图的水量

题目大意:

题解:

动态规划

对于下标\(i\),水能到达的最大高度等于下标\(i\)两边的最大高度的最小值,所以可以用动态规划递推出两边的高度最大值,然后遍历一遍数组,对于下标\(i\)处水能到达的最大高度就等于下标\(i\)两边的最大高度的最小值减去\(height[i]\)
状态转移方程:

\[\left\{ \begin{array}{l} leftMaxHeight[i]=max\{leftMaxHeight[i−1],height[i]\},1\leq i\leq n-1 \\ rightMaxHeight[i]=max\{rightMaxHeight[i+1],height[i]\}, 0\leq i\leq n-2 \end{array} \right. \]

class Solution {
public:
    // 动态规划  计算两边的高度最大值
    int trap(vector<int>& height) {
        int n = height.size();
        if (n == 0) {
            return 0;
        }
        vector<int> leftMaxHeight(n);
        leftMaxHeight[0] = height[0];
        for (int i = 1; i < n; ++i) {
            leftMaxHeight[i] = max(leftMaxHeight[i - 1], height[i]);
        }
        vector<int> rightMaxHeight(n);
        rightMaxHeight[n - 1] = height[n - 1];
        for (int i = n - 2; i >= 0; --i) {
            rightMaxHeight[i] = max(rightMaxHeight[i + 1], height[i]);
        }
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            ans += min(leftMaxHeight[i], rightMaxHeight[i]) - height[i];
        }
        return ans;
    }
};

单调栈

维护一个单调栈,单调栈存储的是下标,满足从栈底到栈顶的下标对应的数组\(height\)中的元素递减。
从左到右遍历数组,遍历到下标\(i\)时,如果栈内至少有两个元素,记栈顶元素为\(top\)\(top\)的下面一个元素是\(left\),则一定有\(height[left] \geq height[top]\)。如果\(height[i]>height[top]\),则得到一个可以接雨水的区域,该区域的宽度是\(i−left−1\),高度是\(min(height[left],height[i])−height[top]\),根据宽度和高度即可计算得到该区域能接的水的量。
在对下标\(i\)处计算能接水的量之后,将\(i\)入栈,继续遍历后面的下标,计算能接的水的量。

class Solution {
public:
    // 单调栈  存下标,一层一层注水
    int trap(vector<int>& height) {
        int ans = 0, n = height.size();
        stack<int> st;
        for (int i = 0; i < n; ++i) {
            while (!st.empty() && height[i] > height[st.top()]) {
                int top = st.top();
                st.pop();
                if (st.empty()) {
                    break;
                }
                int left = st.top();
                int h = min(height[left], height[i]) - height[top];
                int l = i - left - 1;
                ans += l * h;
            }
            st.push(i);
        }
        return ans;
    }
};

尺取法

参考自LeedCode官方题解
动态规划的做法中,需要维护两个数组\(leftMaxHeight\)\(rightMaxHeight\),因此空间复杂度是\(O(n)\)。是否可以将空间复杂度降到\(O(1)\)
注意到下标\(i\)处能接的水的量由\(leftMaxHeight[i]\)\(rightMaxHeight[i]\)中的最小值决定。由于数组\(leftMaxHeight\)是从左往右计算,数组\(rightMaxHeight\)是从右往左计算,因此可以使用双指针和两个变量代替两个数组。
维护两个指针\(left\)\(right\),以及两个变量\(leftMax\)\(rightMax\),初始时\(left=0,right=n−1,leftMax=0,rightMax=0\)。指针\(left\)只会向右移动,指针\(right\)只会向左移动,在移动指针的过程中维护两个变量\(leftMax\)\(rightMax\)的值。
当两个指针没有相遇时,进行如下操作:

  • 使用\(height[left]\)\(height[right]\)的值更新\(leftMax\)\(rightMax\)的值;
  • 如果\(height[left]<height[right]\),则必有\(leftMax<rightMax\),下标\(left\)处能接的水的量等于\(leftMax−height[left]\),将下标\(left\)处能接水的量加到能接水的总量,然后将\(left\)\(1\)(即向右移动一位);
  • 如果\(height[left]\geq height[right]\),则必有\(leftMax \geq rightMax\),下标\(right\)处能接的水的量等于\(rightMax−height[right]\),将下标\(right\)处能接水的量加到能接水的总量,然后将\(right\)\(1\)(即向左移动一位)。

当两个指针相遇时,即可得到能接的水的总量。

class Solution {
public:
    // 双指针
    int trap(vector<int>& height) {
        int ans = 0, left = 0, right = height.size() - 1, leftMax = 0, rightMax = 0;
        while (left < right) {
            leftMax = max(leftMax, height[left]);
            rightMax = max(rightMax, height[right]);
            if (height[left] < height[right]) {
                ans += leftMax - height[left];
                left++;
            } else {
                ans += rightMax - height[right];
                right--;
            }
        }
        return ans;
    }
};
posted @ 2022-02-06 14:09  ZZHHOOUU  阅读(27)  评论(0编辑  收藏  举报