节后第一天,鉴于五一五天都没做过题,有点遗忘了,今天来看一道简单点的题,练下手。
先看下题:
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
暴力法
一般看完这个题优先的思路是暴力法,两层循环嵌套,然后得到最大值,时间复杂度为O(n^2),由于官方已经加大了测试用例的规模,暴力法会超时,已经过不了了。
那我们想想其他的方法吧,在数组和链表两个比较简单的线性表求面积的时候,经常会用到一种思想,双指针,这个后面遇到这类题的时候,没思路可以往这方面想。
双指针
什么叫双指针呢,顾名思义,就是用两个指针(内存里的指针是记录引用地址的内存空间,这个地址会指向引用的实际存储位置),记录左右边界位置,然后按照一定条件移动双指针,达到求最大面积的目的。
以这个题为例,我们要求面积最大值,就需要宽和高足够大就可以,但是我们需要知道柱子的面积最大值,就需要移动左右指针,该怎么定义移动规则呢?
其实很简单,我们假设左指针left=0
,右指针right=a.length-1;
,这时候有V=(right-left)*Math.min(a[left],a[right])
,然后我们这时候想要得到比这个面积大的区域,首先left和right一定会往中间走,宽一定是会变小的,要使面积变大,就一定要高竟可能大,因此我们舍弃高较小的那根柱子,然后不断重复上述过程,直到left==right。
我们不难发现,利用双指针法,成功将时间复杂度由O(n^2)降为了O(n).所以可以知道双指针法能减少一层遍历。
思路想明白之后代码实现就很简单了,下面是其中一种实现:
int max = 0;
for (int left = 0, right = a.length - 1; left < right; ) {
int v = (right - left) * (a[left] < a[right] ? a[left++] : a[right--]);
max = max < v ? v : max;
}
return max;
写在最后
数组相关的题大多比较简单,很多都是一些固定的模式,一开始可能觉得很神奇,这东西还能这么玩?但是摸清楚套路之后感觉也就那样,所以数组相关的题没啥注意的,多练就行。