盛水问题
11. 盛最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
示例:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
思路:
我们使用双指针来解决。
left
和right
两个指针从两端向中心收缩,一边收缩一边计算[left,right]
之间的矩阵面积,取最大的面积即是答案。
现在关键是要向那边收缩?
我们知道,矩形的高度是由最短的边决定的。所以移动较低的那条边,矩阵的高度可能会变高。
min(height[left],height[right])
。如果移动高度较高的边,则矩阵的高度不可能变高。
1 | class Solution { |
42. 接雨水
给定 n
个非负整数表示每个宽度为 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 个单位的雨水(蓝色部分表示雨水)。
思路:
方法一:单调栈
具体的思想可以看单调栈
我们把下标入栈,分别淹没现在所在的这一行元素中可以淹没的。
我们只需要判断此时矩阵中需要填的水,累加就是答案,矩阵的高是由左边界和右边界的最小值决定的,我们需要注意的是,我们是每次处理一层,所以要把左边界和右边界中的最小值减去要弹出的元素的高度。
1 | int top = s.top(); |
完整代码如下:
1 | class Solution { |
时间复杂度和空间复杂度都为O(N)
方法二:双指针
我们先明确几个变量的意思:
left_max:左边的最大值,它是从左往右遍历找到的
right_max:右边的最大值,它是从右往左遍历找到的
left:从左往右处理的当前下标
right:从右往左处理的当前下标
定理一:在某个位置i
处,它能存的水,取决于它左右两边的最大值中较小的一个。
定理二:当我们从左往右处理到left下标时,左边的最大值left_max对它而言是可信的,但right_max对它而言是不可信的。(见下图,由于中间状况未知,对于left下标而言,right_max未必就是它右边最大的值)
定理三:当我们从右往左处理到right下标时,右边的最大值right_max对它而言是可信的,但left_max对它而言是不可信的。
1 | right_max |
对于位置left
而言,它左边最大值一定是left_max,右边最大值“大于等于”right_max,这时候,如果left_max<right_max
成立,那么它就知道自己能存多少水了。无论右边将来会不会出现更大的right_max,都不影响这个结果。
所以当left_max<right_max
时,我们就希望去处理left下标,反之,我们希望去处理right下标。
1 | class Solution { |
时间复杂度为O(N)
空间复杂度为O(1)
。