LeetCode —— 42「接雨水」

61 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第35天,点击查看活动详情

一、题目

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

二、示例

示例 1:

image.png

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 

示例 2:

输入: height = [4,2,0,3,2,5]
输出: 9

三、题解

双指针

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;
    }
}

复杂度分析

解题思路

方案一:二分查找

动态规划的做法中,需要维护两个数组 \textit{leftMax}leftMax 和 \textit{rightMax}rightMax,因此空间复杂度是 O(n)O(n)。是否可以将空间复杂度降到 O(1)O(1)?

注意到下标 ii 处能接的雨水量由 \textit{leftMax}[i]leftMax[i] 和 \textit{rightMax}[i]rightMax[i] 中的最小值决定。由于数组 \textit{leftMax}leftMax 是从左往右计算,数组 \textit{rightMax}rightMax 是从右往左计算,因此可以使用双指针和两个变量代替两个数组。

维护两个指针 \textit{left}left 和 \textit{right}right,以及两个变量 \textit{leftMax}leftMax 和 \textit{rightMax}rightMax,初始时 \textit{left}=0,\textit{right}=n-1,\textit{leftMax}=0,\textit{rightMax}=0left=0,right=n−1,leftMax=0,rightMax=0。指针 \textit{left}left 只会向右移动,指针 \textit{right}right 只会向左移动,在移动指针的过程中维护两个变量 \textit{leftMax}leftMax 和 \textit{rightMax}rightMax 的值。

当两个指针没有相遇时,进行如下操作:

使用 \textit{height}[\textit{left}]height[left] 和 \textit{height}[\textit{right}]height[right] 的值更新 \textit{leftMax}leftMax 和 \textit{rightMax}rightMax 的值;

如果 \textit{height}[\textit{left}]<\textit{height}[\textit{right}]height[left]<height[right],则必有 \textit{leftMax}<\textit{rightMax}leftMax<rightMax,下标 \textit{left}left 处能接的雨水量等于 \textit{leftMax}-\textit{height}[\textit{left}]leftMax−height[left],将下标 \textit{left}left 处能接的雨水量加到能接的雨水总量,然后将 \textit{left}left11(即向右移动一位);

如果 \textit{height}[\textit{left}] \ge \textit{height}[\textit{right}]height[left]height[right],则必有 \textit{leftMax} \ge \textit{rightMax}leftMax≥rightMax,下标 \textit{right}right 处能接的雨水量等于 \textit{rightMax}-\textit{height}[\textit{right}]rightMax−height[right],将下标 \textit{right}right 处能接的雨水量加到能接的雨水总量,然后将 \textit{right}right11(即向左移动一位)。

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

下面用一个例子 \textit{height}=[0,1,0,2,1,0,1,3,2,1,2,1]height=[0,1,0,2,1,0,1,3,2,1,2,1] 来帮助读者理解双指针的做法。

算法

原题:LeetCode