今天在leetcode上刷了一道题用到了双指针,现在把这种精妙的解法记录下来
题目传送门
题目的大意就是要找到一个最大的min(s[i], s[j])*(j-i),那么这时候我是一点思路都没,然后题解指出了一个双指针的做法,我们先讨论这个东西的合理性,初始情况,将i,j放到数组的开始和结尾,此时,我们能拿到一个面积,这个面积的计算公式是:
min(s[left], s[right])*(right-left)
那么,接下来怎么做呢,不妨假设一边不动,挪动另一边的下标,如果改变left的下标,不妨看出,此时(right-left)减小,新得到的s[left],有以下的情况
- old-left < new-left 面积可能变大
- old-left > new-left 面积缩小
那如果让right的下标左移呢,此时也会有几种情况 - old-right < new-right || old-right > new-right > left面积缩小—>因为right-left减小了
- old-right > left > new-right 面积缩小
所以这时候,我们能得到第一条公理,当前状态下,为了保证下一步变化能使得围成的面积朝着变大的方向变化,只能去移动长度较小的那个指针,那么真的就这么简单吗?
并不是,这里还蕴藏着一些问题,指针移动究竟意味着什么呢?
左指针的移动意味着,他和位于其他任何位置的右指针所围成的面积都已经不可能超过当前这个面积了
右指针的移动意味着,他和位于其他任何位置的左指针所围成的面积都已经不可能超越当前这个面积了
所以这里会出现第二个公理,那就是任何左指针只能向着增大的方向运动,任何右指针只能向着减小的方向运动,这个是我最开始很困惑的一个问题,为什么一直是left++,right–,其实原因是,任何一个左指针一定是从上一步右移过来的,如果left–就意味着你往回走了,但是已经论证过了当前位置的上一个位置的左指针与任何其他位置的右指针所围成的面积,都不可能超过一个最大面积了,而这个面积已经被记录了,所以往回走是没有意义的,所以说,左指针++,右指针–,这就是唯一的方向,不存在回头路,因为那不会是最优解,至此,思路就已经很清晰了 - 获取到当前指针位置下,围成的面积,与之前的最大值比较,更新最大值
- 移动当前指针中比较短的一个
-更新左右指针,直到二者重合
下面是大佬写的题解
大佬题解
下面是我的代码
class Solution {
public int maxArea(int[] height) {
int left = 0, right = height.length-1, ans = 0;
while(true) {
if(left>=right) {
break;
}
int min = height[left] <= height[right] ? height[left] : height[right];
int now = (right-left) * min;
if(ans < now) {
ans = now;
}
if(height[left] < height[right]) {
left++;
}else {
right--;
}
}
return ans;
}
}