Skip to content

柱子

接水

11. 盛最多水的容器 mid

给定一个长度为 n 的整数数组 height。有 n 条垂线, 第 i 条线的两个端点是 (i, 0) 和 (i, height[i])。
You are given an integer array height of length n.
There are n vertical lines drawn such that the two endpoints of the ith line are (i, 0) and (i, height[i]).

找出其中的两条线, 使得它们与 x 轴共同构成的容器可以容纳最多的水。
Find two lines that together with the x-axis form a container, such that the container contains the most water.

返回容器可以储存的最大水量。
Return the maximum amount of water a container can store.

说明:你不能倾斜容器。
Notice that you may not slant the container.

短板决定了面积, 将短的那一侧向中间移动; 如果两边一样长, 那么只移动一侧面积都不会更大, 所以移动两端

java
class Solution {
    public int maxArea(int[] height) {
        int p1 = 0, p2 = height.length - 1;
        int res = 0;
        while (p1 < p2) {
            int square;
            if (height[p1] < height[p2])
                square = height[p1] * (p2 - p1++);
            else if (height[p1] > height[p2])
                square = height[p2] * (p2-- - p1);
            else
                square = height[p1] * (p2-- - p1++);
            res = Math.max(res, square);
        }
        return res;
    }
}
go
func maxArea(height []int) int {
    l,r := 0,len(height)-1
    res := 0
    for l < r {
        res = max(res, min(height[l],height[r])*(r-l))
        if height[l] < height[r] {
            l++
        } else if height[l] > height[r] {
            r--
        } else {
            l++
            r--
        }
    }
    return res
}
func min (a int, b int) int {
    if a < b {
        return a
    }
    return b
}
func max (a int, b int) int {
    if a > b {
        return a
    }
    return b
}

42. 接雨水 hard

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

解法 1: 单调栈

要模拟去想, 遍历过程中, 当前元素首先作为右端参与计算, 然后作为左端入栈;
如果当前元素比前一个元素高 & 前一个元素前面还有, 计算面积;

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

解法 2: 动态规划-前缀函数

跟 3 元组有点像, 用前缀函数记录前后的最大值, 就能计算当前柱子的积水

java
class Solution {
    public int trap(int[] arr) {
        int n = arr.length;
        int res = 0;
        int[] heigh = new int[n];
        heigh[n - 1] = arr[n - 1];
        for (int i = n - 2; i >= 0; i--) {
            heigh[i] = Math.max(arr[i], heigh[i + 1]);
        }
        int pre = arr[0];
        for (int i = 1; i < n - 1; i++) {
            int diff = Math.min(pre, heigh[i + 1]) - arr[i];
            res += Math.max(diff, 0);
            pre = Math.max(pre, arr[i]);
        }
        return res;
    }
}

解法 3: 双指针

利用当前柱子积水由左右两侧高值的较小一方决定, 可以维护双指针和 2 个最大值(表示双指针左右两侧的最大值)
迭代过程中, 最大值较小的一侧可以计算该指针所指向柱子的积水; 相反的一侧因为不知道中间是否有更高的柱子, 所以不能计算

java
class Solution {
    public int trap(int[] arr) {
        int n = arr.length;
        int res = 0;
        int l = 1, r = n - 2;
        int lmax = arr[0], rmax = arr[n - 1];
        while (l <= r) {
            if (lmax < rmax) {
                res += Math.max(0, lmax - arr[l]);
                lmax = Math.max(lmax, arr[l]);
                l++;
            } else {
                res += Math.max(0, rmax - arr[r]);
                rmax = Math.max(rmax, arr[r]);
                r--;
            }
        }
        return res;
    }
}

407. 接雨水 II hard

给你一个 m x n 的矩阵,其中的值均为非负整数,代表二维高度图每个单元的高度,请计算图中形状最多能接多少体积的雨水。

解法

从最外缘一圈入优先队列, 出队的元素决定了相邻 4 向柱子的积水, 入队要入 max(水位,柱子高度)

java
class Solution {

    public int trapRainWater(int[][] heightMap) {
        if (heightMap == null || heightMap.length < 3 || heightMap[0].length < 3) return 0;

        int rowCnt = heightMap.length;
        int colCnt = heightMap[0].length;
        // [0]=r [1]=c [2]=水位/柱子高度
        PriorityQueue<int[]> que = new PriorityQueue<>(Comparator.comparingInt(o -> o[2]));

        for (int r = 0; r < rowCnt; r++) {
            que.add(new int[]{r, 0, heightMap[r][0]});
            que.add(new int[]{r, colCnt - 1, heightMap[r][colCnt - 1]});
            heightMap[r][0] = -1;
            heightMap[r][colCnt - 1] = -1;
        }

        for (int c = 1; c < colCnt - 1; c++) {
            que.add(new int[]{0, c, heightMap[0][c]});
            que.add(new int[]{rowCnt - 1, c, heightMap[rowCnt - 1][c]});
            heightMap[0][c] = -1;
            heightMap[rowCnt - 1][c] = -1;
        }

        int res = 0;
        int[] dr = {1, -1, 0, 0};
        int[] dc = {0, 0, 1, -1};

        while (!que.isEmpty()) {
            int[] point = que.remove();
            int tmpR = point[0];
            int tmpC = point[1];
            int tmpH = point[2];
            for (int k = 0; k < 4; k++) {
                int newR = tmpR + dr[k];
                int newC = tmpC + dc[k];
                if (newR >= 0 && newR < rowCnt
                        && newC >= 0 && newC < colCnt
                        && heightMap[newR][newC] != -1) {
                    // 入队就计算
                    res += Math.max(0, tmpH - heightMap[newR][newC]);
                    que.add(new int[]{newR, newC, Math.max(tmpH, heightMap[newR][newC])});
                    heightMap[newR][newC] = -1;
                }
            }
        }
        return res;
    }
}

柱子面积

84. 柱状图中最大的矩形 hard

LCR 039. 柱状图中最大的矩形

给定 n 个非负整数, 用来表示柱状图中各个柱子的高度。每个柱子彼此相邻, 且宽度为 1。

求在该柱状图中, 能够勾勒出来的矩形的最大面积。

解法 单调栈

找到元素 i 左右比 i 低的元素索引

java
class Solution {
    public int largestRectangleArea(int[] h) {
        int n = h.length;
        int[] left = new int[n];
        int[] right = new int[n];
        Deque<Integer> stack = new ArrayDeque<>();
        // 如果没 pop, 说明 right[i] = n-1
        Arrays.fill(right, n - 1);
        for (int i = 0; i < n; i++) {
            while (!stack.isEmpty() && h[i] < h[stack.element()]) {
                right[stack.pop()] = i - 1;
            }
            if (!stack.isEmpty()) left[i] = stack.element() + 1;
            stack.push(i);
        }
        int max = 0;
        for (int i = 0; i < n; i++) {
            max = Math.max(max, h[i] * (right[i] - left[i] + 1));
        }
        return max;
    }
}