前言
盛最多水的容器 接雨水_哔哩哔哩_bilibili 灵茶山艾府大佬牛!
还是双指针,用法是上一讲的扩展,接雨水难度爆炸。
上一节的双指针偏套路,而这节的话思路非常重要。
提示:以下是本篇文章正文内容,下面案例可供参考
一、Leetcode11. 盛最多水的容器
给定一个长度为 n
的整数数组 height
。有 n
条垂线,第 i
条线的两个端点是 (i, 0)
和 (i, height[i])
。
找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7] 输出:49 解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1] 输出:1
解题思路 题目说找两根线,第i根线的长度就是heigh[i],然后求两根线围起来的长方形的最大面积。 那么这个长方形的宽就是 min(height[i],height[j]) ,长方形的长就是 j - i。
长方形的长就是 j - i,这个事是从图上看出来的,假设这个长方形取第一根线和最后一根线,可以发现长方形的长度是 9 - 1。
先用暴力试了一下,61个样例过了50,过不去。
法1:暴力法
就是取一根线然后依次和第二根线、第三根。。。组合求面积,如果面积大了,就更新。
class Solution:
def maxArea(self, height: List[int]) -> int:
maxarea = 0
for i in range(len(height) - 1):
x = height[i]
for j in range(i+1, len(height)):
y = height[j]
maxarea = max(maxarea, min(x, y)*(j - i))
return maxarea
法2:双指针法
这里是一个很巧妙的做法,之前我的想法是应用双指针必须对数组进行排序,其实不然。
当我们枚举两个端点的数值的时候,我们取到了最大的长(right - left)! 但是我们的宽呢,是两个线长中较小的那一段min(height[left], height[right])。其实就可以把较小的那根线排除了,因为接下来不管怎么枚举,我们的长度肯定是缩减的(right - left),但是高度又不可能高过较小线的高度,因此排除较小的线。接下来的高度就有可能高过之前的高度。
class Solution:
def maxArea(self, height: List[int]) -> int:
n = len(height)
left = 0
right = n - 1
maxarea = 0
while left < right:
maxarea = max(maxarea, (right-left)*min(height[left], height[right]))
if height[left] < height[right]:
left = left + 1
else:
right = right - 1
return maxarea
二、Leetcode42. 接雨水
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
输入: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
解题思路 这题我自己完全想不出,只能听课,看了两遍才看懂。
法1: 前后缀的方法
想象每个位置都有一个水桶,那么这个水桶的容量就是通过min(他左边板子的高度最大值,右边板子的高度最大值)算出来的。
这也就是前缀和以及后缀和,把每个位置的前缀和 和 后缀和算出来,然后取最小值就得到了当前位置的水桶容量。 然后用水桶的容量 - 当前位置的高度height[i] 就能算出当前位置的储水量。
class Solution:
def trap(self, height: List[int]) -> int:
n = len(height)
pre_max = [0] * n
pre_max[0] = height[0]
ans = 0
for i in range(1, n):
pre_max[i] = max(pre_max[i-1], height[i])
suf_max = [0] * n
suf_max[-1] = height[-1]
for i in range(n-2, -1, -1):
suf_max[i] = max(suf_max[i+1], height[i])
for pre, suf, h in zip(pre_max, suf_max, height):
ans += min(pre, suf) - h
return ans
法2:双指针法
初始化 前缀和后缀,因为前缀和后缀有一个 特点 那就是他的值是不会变小的,也就是说
当 pre_max < suf_max 的时候, 左指针指向的水桶的容量就已经固定了,因为取得是两者的较小值。 这个时候 ans += pre_max - height[left] left = left + 1
当 suf_max < pre_max 的时候,右指针指向的水桶的容量就已经固定了,因为取得是两者的较小值。 这个时候 ans += suf_max - height[right] right = right - 1
循环条件 left < right,这个大家可以画图思考一下,当left 和 right 相等的时候,两个指针都指向了最高的水桶,两者相等肯定是无法接水的,故循环结束。
class Solution:
def trap(self, height: List[int]) -> int:
left, right = 0, len(height) - 1
ans = 0
pre_max, suf_max = 0, 0
while left < right:
pre_max = max(pre_max, height[left])
suf_max = max(suf_max, height[right])
if pre_max > suf_max:
ans += suf_max - height[right]
right -= 1
else:
ans += pre_max - height[left]
left += 1
return ans
总结
这节是对相向双指针的进一步应用