Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.數組
給定n條線段,每條線段的端點爲(i,0)和(i,a[i]),如今須要在這n條線段中選擇兩條並與x軸組成一個水桶,求最大容量的水桶的容量(面積)。spa
暴力的方法就是直接枚舉兩條線段,找最大面積,這樣複雜度是O(n^2)的。(沒想到leetOJ竟然還有稍強的數據,感受是故意卡死這個複雜度的方法的。。)指針
我換了一個思路後,想到因爲最後的最大面積必定是以某一個線段爲桶的一側(多是左側或者右側)而且恰好淹沒這條線段,那咱們只須要枚舉每條線段,而後找出這條線段左側(或者右側)比它高的最左(最右)的一個線段的位置,而後計算面積,並記錄最大面積便可。code
找到左側比當前線段要高的最左側線段的方法,能夠直接將每一個線段先按高度從大到小,再按座標從小到大排序,而後掃描數組,記錄 到當前的最小座標,因爲後掃描到的線段高度必定比先掃描到的高度小,因此只須要記錄前面的座標的最小值,即是當前線段的最左側線段的座標。時間複雜度O(nlogn)空間O(n)。orm
官網給出的標準方法,因爲智力有限並不能想到,可是對其正確性仍是能夠給出證實。方法以下:blog
令指針i,j指向數組的開頭和結尾,而後每次選取height[i]和height[j]較小的那個指針,並將指針想中間位置移動覺得,並從新計算面積。(時間複雜度O(n),空間複雜度O(1))。排序
雖然我不能對這個方法有比較好的啓發式的思惟來想到,可是我至少仍是得對其正確性給出證實,個人方法以下:get
令a(i,j)表示線段i和j組成的面積(即a(i,j)=(j - i) * min{height(i), height(j)}),令DP(i,j)表示區間[i,j]的最大答案,則有:it
DP(i,j) = max{DP(i+1,j), DP(i, j-1), a(i, j)}io
只要證實當height(i) <= height(j)時,DP(i,j) = max{DP(i+1,j),a(i,j)}恆成當即可。
證實過程只須要分狀況討論便可:
一、若a(i,j) == DP(i,j) 這時結論顯然成立。
二、若a(i,j) < DP(i,j)
1) 若DP(i+1, j-1) == DP(i,j) 這時,因爲DP(i+1,j) >= DP(i+1, j-1),這時結論成立。
2) 若DP(i+1, j-1) < DP(i,j) 這時有DP(i,j) = max{DP(i+1,j), DP(i, j-1)}
由於DP(i + 1,j) = max{DP(i+1,j-1), a(i+1,j), a(i+2, j), ... , a(j - 1, j), a(j, j)}
同時DP(i, j - 1) = max{DP(i+1,j-1), a(i, i), a(i,i+1), a(i, i+2), ... , a(i, j - 1)}
由於height(i) <= height(j) 因此有max{a(i, i), a(i,i+1), a(i, i+2), ... , a(i, j - 1)} <= a(i, j) < DP(i, j)恆成立。
因此 DP(i, j) = DP(i+1, j)。得證。
1 public class Solution { 2 public int maxArea(int[] height) { 3 int maxarea = 0, l = 0, r = height.length - 1; 4 while (l < r) { 5 maxarea = Math.max(maxarea, Math.min(height[l], height[r]) * (r - l)); 6 if (height[l] < height[r]) 7 l++; 8 else 9 r--; 10 } 11 return maxarea; 12 } 13 }