前面在刷leetCode时,更多的还是用以前的思维方式去解决问题。当然了,我也不知道以前的的思维方式叫什么名字,反正不好,让我写出一些糟糕的代码。而就是这个题目,在我的不断思考下,竟有了双指针的影子。
题目简介
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明: 你不能倾斜容器。
我的第一个回答
思路: 从左边第一个起,每一个柱子都和右边的其他柱子计算面积 ,最后找到最大的。}
1号和2号计算面积
高度:
宽度:
面积:
1号和3号计算面积
高度:
宽度:
面积:
... 省略中间
1号和9号计算面积
高度:
宽度:
面积:
2号和3号计算面积(由于2号和1号计算过,便不再计算)
高度:
宽度:
面积:
2号和4号计算面积
高度:
宽度:
面积:
2号和9号计算面积
高度:
宽度:
面积:
依次这样下去,使用一个变量记录面积最大值,即可。
时间复杂度:
function maxArea(height: number[]): number {
interface ElementInterface {
value: number;
idx: number;
}
//max用于保存最大面积
let max = 0;
for (let i = 0; i < height.length; i++) {
for (let j = i + 1; j < height.length; j++) {
const left = {
value: height[i],
idx: i,
},
right = {
value: height[j],
idx: j,
};
//Math.max() 输入若干个数字,返回最大的
max = Math.max(getArea(left, right), max);
}
}
//获取面积
function getArea(left: ElementInterface, right: ElementInterface): number {
const width = right.idx - left.idx;
const height = left.value <= right.value ? left.value : right.value;
return width * height;
}
return max;
}
提交到leetCode,说我时间用的太多了,好吧,我一看数据,数组里有7万多个数据,我这双层循环,执行次数高达上亿次。这确实不行}。
我的第二个回答
我重新思考了下,是否所有的计算都是有意义的,如果提前就能预知某些面积就是小于其他的面积,那就可以不用计算了。
我们可以再看下这张图,图中的2号和4号计算面积。由于高度为两个柱子,所以高度就 = 4号的高度。而宽度是索引的差距。
这时候,当我们开始用3号依次和后面的计算面积时,其结果必然小于2号和后面计算的面积。因为3号的高度低于2号,而索引距离右边的柱子要比2号近1个,所以宽度也比2号小1。
之后的原理都如此,如果右边的柱子没有左边的高,则该柱子和右边其他柱子所形成的面积必然小于左侧(相同高度或更高)的柱子与右侧其他柱子形成的面积。
function maxArea(height: number[]): number {
interface ElementInterface {
value: number;
idx: number;
}
let max = 0; //保存着最大面积(存水量)
let maxHeight = 0; //保存着最高柱子的高度(值)
for (let i = 0; i < height.length; i++) {
//,如果后面的高度小于前面的高度,则没有必要计算,因为它的宽度没有前面的大
if (height[i] <= maxHeight) {
continue;
}
maxHeight = height[i];
for (let j = i + 1; j < height.length; j++) {
const left = {
value: height[i],
idx: i,
},
right = {
value: height[j],
idx: j,
};
max = Math.max(getArea(left, right), max);
}
}
function getArea(left: ElementInterface, right: ElementInterface): number {
const width = right.idx - left.idx;
const height = left.value <= right.value ? left.value : right.value;
return width * height;
}
return max;
}
最终回答——双指针
我们再看下最初的那张图,我们先创建两个指针,。
当计算完2号和9号的面积后,我们将右侧指针向内移动。因为高度为最小高度决定,所以如果移动左侧的,其面积必然会减少。移动后如下。
这时候,2号和8号的面积小于之前的,继续移动右侧指针。
之后,一直移动两指针中高度小的那个,直到相遇。
function maxArea(height: number[]): number {
let max = 0; //最大面积
let leftIdx = 0; //左指针
let rightIdx = height.length - 1; //右指针
while (leftIdx < rightIdx) {
const w = rightIdx - leftIdx; //宽
const h = Math.min(height[leftIdx], height[rightIdx]); //高
const area = w * h;
max = Math.max(max, area);
//移动指针
height[leftIdx] > height[rightIdx] ? rightIdx-- : leftIdx++;
}
return max;
}