題目
給定 n 個非負整數 a1,a2,…,an,每個數代表座標中的一個點 (i, ai) 。在座標內畫 n 條垂直線,垂直線 i 的兩個端點分別爲 (i, ai) 和 (i, 0)。找出其中的兩條線,使得它們與 x 軸共同構成的容器可以容納最多的水。
說明:你不能傾斜容器,且 n 的值至少爲 2。
圖中垂直線代表輸入數組 [1,8,6,2,5,4,8,3,7]。在此情況下,容器能夠容納水(表示爲藍色部分)的最大值爲 49。
示例:
輸入: [1,8,6,2,5,4,8,3,7]
輸出: 49
解法一(暴力法)
思路:直接兩層遍歷,分別計算任意兩個桶之間的容積,然後記錄最大的結果,作爲中等難度的題目,這個方法可能會超時,最終不出所料在最後一個測試用例超時了,超時用例爲15000個從大到小排列的數值,測試用例及源碼如下:
https://www.zhenxiangsimple.com/files/tech/testCase20200208.txt
- 第一層遍歷作爲桶的左側,第二層遍歷,作爲桶的右側
- 選擇較小的值乘以間距,計算桶的容積
- 時間複雜度:O(n2)
- 空間複雜度:O(1)
public class Solution {
public int MaxArea(int[] height) {
int t,mx=0;
for(int i=0;i<height.Length;i++)
{//桶左側
t = 0;
for(int j=i+1;j<height.Length;j++)
{//桶右側
t = (j-i) * (height[i]>height[j]?height[j]:height[i]);//計算容積
if(t > mx)
{
mx = t;
}
}
}
return mx;
}
}
解法二(從外向裏)
思路:用兩個指針分別從兩邊向中間進行查找,如果全部遍歷就跟暴力法一樣了,本算法使用較小的值向較大值的方向移動,因爲X座標變小後只有Y座標變大才可能變得更大,因此選擇捨棄較小(短)的Y值,向較大的方向移動來尋找更大的值。
- 分別使用左右兩個指針,從數組開始和結尾開始遍歷
- 值小的一邊向另一邊移動,直到兩個指針相遇
- 時間複雜度:O(n)
- 空間複雜度:O(1)
public class Solution {
public int MaxArea(int[] height) {
int l=0,r=height.Length-1,t,mx=0;
while(l<r)
{
if(height[l] > height[r])
{//右邊小,右指針向左移動
t = height[r] * (r-l);
r--;
}
else
{//左邊小,左指針向右移動
t = height[l] * (r-l);
l++;
}
if(t>mx)
{//記錄最大值
mx = t;
}
}
return mx;
}
}
解法三(從裏向外)
思路:基於解法二的由外向裏的過程,自然想到從上而下的思路,首先選擇兩個最高的值,然後桶外側找最大值,因爲高度減小了只有向外才能使得容積變大。
- 找到最大值和次大值的索引,作爲桶的左右邊界
- 在桶外面找最大的一個值,記錄桶的容積,直到數組遍歷結束
- 這個跟解法二不同之處是,不是選其中一邊裏的做大值,而是兩邊的最大值,因爲可能由於選擇了較小的值導致拉低高度值
- 時間複雜度:O(n2)
- 空間複雜度:O(1)
public class Solution {
public int findMaxIdx(int[] height,int l,int r)
{ //尋找[l,r)內最大索引
int ti = l;
for(int i=l + 1;i<r;i++)
{
if(height[i] > height[ti])
{
ti = i;
}
}
return ti;
}
public int findOutIdx(int[] height,int l,int r,out bool lFlag)
{//尋找[l,r]外面最大索引
lFlag = true;
int li = -1;
if(l > 0)
{
li = findMaxIdx(height,0,l);
}
if(r < height.Length - 1)
{
int ri = findMaxIdx(height,r+1,height.Length);
if(li==-1 || height[li] < height[ri])
{
lFlag = false;
li = ri;
}
}
return li;
}
public int MaxArea(int[] height) {
int t,mx=0,len = height.Length;
int li=0,ri=-1,ti,tr=-1;
bool flag;
for(int i=1;i<len;i++)
{//li最大值,ri次大值
if(height[i] > height[li])
{//比最大值還大
tr = height[li];
ri = li;
li = i;
}
else if(height[i] > tr)
{//介於中間
tr=height[i];
ri = i;
}
}
if(li > ri)
{//li爲左邊界,ri爲右
t = li;
li = ri;
ri = t;
}
mx = (height[ri] > height[li]?height[li]:height[ri]) * (ri-li);
while(ri<len-1 || li>0)
{//交替切換
ti = findOutIdx(height,li,ri,out flag);
if(flag)
{//向左拓寬了
li = ti;
}
else
{//向右
ri = ti;
}
t = (height[ri] > height[li]?height[li]:height[ri]) * (ri-li);
mx = t>mx?t:mx;
}
return mx;
}
}