给定n个非负整数a1、a2、a3...an,没个数代表坐标中的一个点(i,ai),在坐标轴x上作n条垂线,垂直线i的两个端点分别为(i,ai)和(i,0),找出其中的两条线,使得其与x轴共同构成的容器在不倾斜的情况下可以容纳最多的水。
示例一:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
示例二:
输入:height = [1,1]
输出:1
示例三:
输入:height = [4,3,2,1,4]
输出:16
示例四:
输入:height = [1,2,1]
输出:2
需说明的内容如下:
-
n = height.length。
-
2 <= n <= 3 * 104。
-
0 <= height[i] <= 3 * 104。
针对示例一的输入和输出做一个说明,如图:
上图中垂直线代表输入数组[1,8,6,2,5,4,8,3,7],此时容器能够容纳水(表示为蓝色部分)的最大值为49。
如果这么表达比较不清晰的话,我们可以换个角度来理解,还是上图中的内容,我们既然要求是容水量最大,那肯定是需要计算这个容器的容积,但是按题目中的要求来看,明显只有高和长或是高和宽,相当于一个容器的切面,在这种情况下,如果想拿到最大的容水量,那就不能只考虑这个容器的高度,还得考虑这个容器的长或宽,只有高和长或是高和宽相乘为最大值时,方可取出题目中所要求的数据。
既然目标已经明确了,我们现在要做的就是确认那个是高,那个是长或是宽,很明显,数组中的每个元素就相当于不同的高,那么长或是宽的奥秘就在于数组的元素的位置,比如第一次出现8和最后一次出现的7这两个元素,它们中间相差七个位置,而水容量在不倾斜容器的情况下取决于容器的最低端,所以我们就可以就得出来它的水容量为49。
解决上述问题我这边能想到的最简单的方式就是暴力遍历做对比,如下:
func maxArea(height []int) int {
n := len(height)
if n <= 1 {
return 0
}
maxMulti := 0
for i := 0; i < n-1; i++ {
for j := i + 1; j < n; j++ {
w := j - i // 宽
h := min(height[i], height[j]) // 高
maxMulti = max(maxMulti, w*h) // 比较面积
}
}
return maxMulti
}
网上找了一种优雅的方式-双指针法,它的原理就是两条线段之间的面积受限与最短的线段,线段间距越长,面积越大,使用 2 个指针指向首部和尾部,将短指针向长指针方向移动,看能不能找到更长的线,使面积更大,这种原理的依据是向长线方向每次移动1格,虽然宽度-1,但是(高度变高)*(宽度-1) >= 高度*宽度,实现如下:
func maxArea(height []int) int {
maxMulti := 0
left, right := 0, len(height)-1
for left < right {
w := right - left
h := min(height[left], height[right])
maxMulti = max(maxMulti, w*h)
if height[left] <= height[right] {
left++ // 往右边走找更长的线
} else {
right-- // 往左边走
}
}
return maxMulti
}
又是一种较为高深的说法。。。
至此,本次分享就结束了,后期会慢慢补充。
以上仅为个人观点,不一定准确,能帮到各位那是最好的。
好啦,到这里本文就结束了,喜欢的话就来个三连击吧。
扫码关注公众号,获取更多优质内容。