相向双指针
左右两个指针,根据当前状态移动指针
盛最多水的容器
给定一个长度为 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。
解法分析
左右指针,假设右边值小于左边,此时需要右指针左移
- 左指针右移(无论如何都会获得更少的雨水)
- 左指针的值变小了,此时雨水小于指针移动前的雨水
- 左指针变大了,此时雨水小于移动前的雨水
- 右指针左移
- 右指针的值变小了,此时雨水小于指针移动前的雨水
- 右指针变大了,此时雨水有可能大于移动前的雨水
同理,右边值大于左边时,此时需要左指针右移
左右指针相等时,左右指针随便选择一侧移动即可
时间复杂度:
空间复杂度:
function maxArea(height: number[]): number {
let res: number = 0
let left = 0, right = height.length - 1
while (left < right) {
res = Math.max(res, Math.min(height[left], height[right]) * (right - left))
if (height[left] > height[right]) {
right--
} else {
left++
}
}
return res
};
接雨水
给定 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 个单位的雨水(蓝色部分表示雨水)。
解法分析
对于每个位置,能够接多少雨水取决于:
而计算左/右边柱子最大高度,则可以使用计算前缀/后缀的方法进行计算
时间复杂度:
空间复杂度:
function getPreMax(nums: number[]): number[] {
let pre_max: number[] = [],
max = -Infinity
for (let i = 0; i < nums.length; i++) {
max = Math.max(max, nums[i])
pre_max.push(max)
}
return pre_max
}
function getSufMax(nums: number[]): number[] {
let suf_max: number[] = [],
max = -Infinity
for (let i = nums.length - 1; i >= 0; i--) {
max = Math.max(max, nums[i])
suf_max.unshift(max)
}
return suf_max
}
function trap(height: number[]): number {
let res: number = 0,
pre_max: number[] = getPreMax(height),
suf_max: number[] = getSufMax(height)
for (let i = 0; i < height.length; i++) {
res += Math.min(pre_max[i], suf_max[i]) - height[i]
}
return res
}
上面的解法有一个问题,在计算前缀时对数组进行了两次遍历,有没有可能一次遍历完成呢?其实是可以的
假设左右两个指针left, right
- left > right时,此时right位置上的前缀最大值肯定大于left值,而left又大于right,所以right位置上最多能接多少雨水是可以计算出来的
- left < right,同理
function trap(height: number[]): number {
let res: number = 0, pre_max = -Infinity, suf_max = -Infinity
let left = 0, right = height.length - 1
while (left <= right) {
if (height[left] > height[right]) {
suf_max = Math.max(suf_max, height[right])
res += suf_max - height[right]
right--
} else {
pre_max = Math.max(pre_max, height[left])
res += pre_max - height[left]
left++
}
}
return res
}