LeetCode 42. Trapping Rain Water

主要参考思路
全文围绕一个定理展开:在某个位置 i i i 处,它能存的水,取决于它左右两边的最大值中较小的一个

JAVA

① 按行求【超时】

class Solution {
    public int trap(int[] height) {
        int len = height.length;
        int max_height = getMax(height);    // 获取最高层数
        int ans = 0;                        // 总储水量
        for (int i = 1; i <= max_height; ++i) {
            int temp = 0;                   // 临时储水量
            boolean isStart = false;
            for (int j = 0; j < len; ++j) {
                if (isStart && height[j] < i) {
                    temp++;
                }
                if (height[j] >= i) {
                    ans += temp;
                    temp = 0;
                    isStart = true;
                }
            }
        }
        return ans;
    }

    private int getMax(int[] height) {
        int len = height.length;
        int ans = 0;
        for (int i = 0; i < len; ++i) {
            ans = Math.max(ans, height[i]);
        }
        return ans;
    }
}

但这种代码在下面这种情况下会出错
在这里插入图片描述
实际答案是 1 1 1,但因为数组最右边多出了 0 0 0,因此实际输出会变成 2 2 2

② 按列求(动态规划)【时间复杂度: O ( N ) O(N) O(N) 空间复杂度: O ( N ) O(N) O(N)

class Solution {
    public int trap(int[] height) {
        int len = height.length;
        int[] left_max = new int[len];      // 当前位置往左边墙最大的高度
        int[] right_max = new int[len];     // 当前位置往右边墙最大的高度

        for (int i = 1; i <= len - 2; ++i) {
            left_max[i] = Math.max(left_max[i - 1], height[i - 1]);     // left_max[0]自然是 0
        }
        for (int i = len - 2; i >= 1; --i) {
            right_max[i] = Math.max(right_max[i + 1], height[i + 1]);   // right_max[len - 1]自然是 0
        }
        int ans = 0;
        for (int i = 1; i <= len - 2; ++i) {
            int temp_min = Math.min(left_max[i], right_max[i]);
            if (temp_min > height[i]) {
                ans += temp_min - height[i];
            }
        }
        return ans;
    }
}

③ 双指针【时间复杂度: O ( N ) O(N) O(N) 空间复杂度: O ( 1 ) O(1) O(1)

class Solution {
    public int trap(int[] height) {
        int ans = 0;
        int len = height.length;
        int left_max = 0;
        int right_max = 0;
        int left = 1;
        int right = len - 2;
        for (int i = 1; i <= len - 2; ++i) {
            if (height[left - 1] < height[right + 1]) {
                left_max = Math.max(left_max, height[left - 1]);
                if (height[left] < left_max) {
                    ans += left_max - height[left];
                }
                left++;
            } else {
                right_max = Math.max(right_max, height[right + 1]);
                if (height[right] < right_max) {
                    ans += right_max - height[right];
                }
                right--;
            }
        }
        return ans;
    }
}

为什么不一开始判断时用 left_max < right_max

答:因为一开始两者都等于 0,无法进行判断,而 height[left - 1] 是可能成为 left_max 的变量, 同理,height [right + 1] 是可能成为 right_max 的变量,只要保证 height[left - 1] < height[right + 1],那么 left_max 就一定小于 right_max,因此我们可以用 height[left - 1] < height[right + 1] 代替 left_max < right_max

④ 单调栈【时间复杂度: O ( N ) O(N) O(N) 空间复杂度: O ( N ) O(N) O(N)

import java.util.Deque;
import java.util.LinkedList;

public class Solution {
    public int trap(int[] height) {
        if (height == null) {
            return 0;
        }
        Deque<Integer> stack = new LinkedList<>();
        int ans = 0;
        for (int i = 0; i < height.length; i++) {
            while(!stack.isEmpty() && height[stack.peek()] < height[i]) {
                int curIdx = stack.pop();
                // 如果栈顶元素一直相等,那么全都pop出去,只留第一个。
                while (!stack.isEmpty() && height[stack.peek()] == height[curIdx]) {
                    stack.pop();
                }
                if (!stack.isEmpty()) {
                    int stackTop = stack.peek();
                    // stackTop此时指向的是此次接住的雨水的左边界的位置。右边界是当前的柱体,即i。
                    // Math.min(height[stackTop], height[i]) 是左右柱子高度的min,减去height[curIdx]就是雨水的高度。
                    // i - stackTop - 1 是雨水的宽度。
                    ans += (Math.min(height[stackTop], height[i]) - height[curIdx]) * (i - stackTop - 1);
                }
            }
            stack.add(i);
        }
        return ans;
    }
}

单调栈参考文章
虽然但是,我还是看不懂啊!

需要注意的地方是不直接使用 S t a c k Stack Stack,而使用 D e q u e Deque Deque 代替 S t a c k Stack Stack

Python

① 按列求(动态规划)(48ms)

class Solution:
    def trap(self, height: List[int]) -> int:
        length=len(height)
        ans=0
        left_max=[0]*length					# 相当于初始化数组
        right_max=[0]*length
        for i in range(1,length-1):
            left_max[i]=max(height[i-1],left_max[i-1])
        for i in range(length-2,0,-1):		# 加个 -1表示倒着遍历
            right_max[i]=max(height[i+1],right_max[i+1])
        for i in range(1,length-1):
            tmp_max=min(left_max[i],right_max[i])
            if tmp_max>height[i]:
                ans+=tmp_max-height[i]
        return ans

② 双指针(44ms)

class Solution:
    def trap(self, height: List[int]) -> int:
        length=len(height)
        ans=0
        left_max,right_max=0,0
        left,right=1,length-2
        for i in range(1,length-1):			# 可以替换成 while left<=right:
            if height[left-1]<height[right+1]:
                left_max=max(left_max,height[left-1])
                if left_max>height[left]:
                    ans+=left_max-height[left]
                left+=1
            else:
                right_max=max(right_max,height[right+1])
                if right_max>height[right]:
                    ans+=right_max-height[right]
                right-=1
        return ans

③ 单调栈(44ms)

class Solution:
    def trap(self, height: List[int]) -> int:
        n = len(height)
        if n == 0:
            return 0
        stack = []
        ans = 0
        for i in range(n):
            while stack and stack[-1][0] < height[i]:
                tmp = stack.pop()  # 删除的是最右边的元素,因此可作为堆使用
                while stack and stack[-1][0] == tmp[0]:
                    stack.pop()
                if stack:
                    ans += (min(stack[-1][0], height[i])-tmp[0])*(i-stack[-1][1]-1)
            stack.append([height[i], i])
        return ans

本代码的 s t a c k stack stack 是二维的,一维代表索引,二维的 stack[i][0] 代表墙高度,stack[-1][1] 代表下标

判断 s t a c k stack stack 是不是空不需要使用函数,只用 if stack 即可

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页