題目描述
給你 n 個非負整數 a1,a2,...,a``n,每個數代表坐標中的一個點 (i, ai) 。在坐標內畫 n 條垂直線,垂直線 i 的兩個端點分別為 (i, ai) 和 (i, 0) 。找出其中的兩條線,使得它們與 x 軸共同構成的容器可以容納最多的水。
**說明:**你不能傾斜容器。
示例 1:
11.container-with-most-water-question
輸入:\[1,8,6,2,5,4,8,3,7\] 輸出:49 解釋:圖中垂直線代表輸入數組 \[1,8,6,2,5,4,8,3,7\]。在此情況下,容器能夠容納水(表示為藍色部分)的最大值為 49。示例 2:
輸入:height = \[1,1\] 輸出:1示例 3:
輸入:height = \[4,3,2,1,4\] 輸出:16示例 4:
輸入:height = \[1,2,1\] 輸出:2提示:
0 <= height[i] <= 3 * 104難度:支持語言:JavaScript 、Python 、C++ 相關標籤相關企業複雜度分析時間複雜度:由於左右指針移動的次數加起來正好是 n, 因此時間複雜度為 。思路 1(javascript):題目中說找出其中的兩條線,使得它們與 x 軸共同構成的容器可以容納最多的水。 ,因此符合直覺的解法就是固定兩個端點,計算可以承載的水量, 然後不斷更新最大值,最後返回最大值即可。這種算法,需要兩層循環,時間複雜度是 。
代碼(JS):
let max = 0; for (let i = 0; i < height.length; i++) { for (let j = i + 1; j < height.length; j++) { const currentArea = Math.abs(i - j) * Math.min(height[i], height[j]); if (currentArea > max) { max = currentArea; } } } return max;雖然解法效率不高,但是可以通過(JS 可以通過,Python 不可以,其他語言沒有嘗試)。那麼有沒有更優的解法呢?
我們來換個角度來思考這個問題,上述的解法是通過兩兩組合,這無疑是完備的。我們換個角度思考,是否可以:
很顯然這種解法也是完備的,但是似乎時間複雜度還是 , 不要著急,我們繼續優化。
考慮一下,如果我們計算 n-1 長度的面積的時候,是可以直接排除一半的結果的。
如圖:
11.container-with-most-water比如我們計算 n 面積的時候,假如左側的線段高度比右側的高度低,那麼我們通過左移右指針 來將長度縮短為 n - 1 的做法是沒有意義的,因為新形成的面積變成了(n-1) * heightOfLeft, 這個面積一定比剛才的長度為 n 的面積 (n * heightOfLeft) 小。
也就是說最大面積一定是當前的面積或者通過移動短的端點得到。
關鍵點解析Js中文網 - 前端進階資源教程 www.javascriptC.com,typescript 中文文檔Javascript中文網是以前端進階資源教程分享為主的專業網站,包括:前端、大廠面試題、typescript教程、程序人生、React.js……等,以幫助開發者成長為願景的社區
思路 2這道題目看似簡單,做起來才發現不容易。分治法、動態規劃都用不上,要想得到 O(n)O(n) 的解法只有使用雙指針一條路。即使看了答案知道了雙指針解法,你也可能並不清楚這個解法為什么正確。為什麼雙指針往中間移動時,不會漏掉某些情況呢?
如果沒有真正理解題目,即使一次對著答案做出來了,再次遇到這個題目,還是可能做不出來。要理解這道題的正確性和原理,需要從背後的縮減搜索空間的思想去考慮題解。下面我將用圖片解釋這道題的正確性和原理。
思路 3算法流程: 設置雙指針 iii,jjj 分別位於容器壁兩端,根據規則移動指針(後續說明),並且更新面積最大值 res,直到 i == j 時返回 res。
指針移動規則與證明: 每次選定圍成水槽兩板高度 h[i]h[i]h[i],h[j]h[j]h[j] 中的短板,向中間收窄 111 格。以下證明:
設每一狀態下水槽面積為 S(i,j)S(i, j)S(i,j),(0<=i<j<n)(0 <= i < j < n)(0<=i<j<n),由於水槽的實際高度由兩板中的短板決定,則可得面積公式 S(i,j)=min(h[i],h[j])×(j−i)S(i, j) = min(h[i], h[j]) × (j - i)S(i,j)=min(h[i],h[j])×(j−i)。
在每一個狀態下,無論長板或短板收窄 111 格,都會導致水槽 底邊寬度 −1-1−1:
若向內移動短板,水槽的短板 min(h[i],h[j])min(h[i], h[j])min(h[i],h[j]) 可能變大,因此水槽面積 S(i,j)S(i, j)S(i,j) 可能增大。
若向內移動長板,水槽的短板 min(h[i],h[j])min(h[i], h[j])min(h[i],h[j]) 不變或變小,下個水槽的面積一定小於當前水槽面積。
因此,向內收窄短板可以獲取面積最大值。換個角度理解:
若不指定移動規則,所有移動出現的 S(i,j)S(i, j)S(i,j) 的狀態數為 C(n,2)C(n, 2)C(n,2),即暴力枚舉出所有狀態。
在狀態 S(i,j)S(i, j)S(i,j) 下向內移動短板至 S(i+1,j)S(i + 1, j)S(i+1,j)(假設 h[i]<h[j]h[i] < h[j]h[i]<h[j] ),則相當於消去了 S(i,j−1),S(i,j−2),...,S(i,i+1){S(i, j - 1), S(i, j - 2), ... , S(i, i + 1)}S(i,j−1),S(i,j−2),...,S(i,i+1) 狀態集合。而所有消去狀態的面積一定 <=S(i,j)<= S(i, j)<=S(i,j):
短板高度:相比 S(i,j)S(i, j)S(i,j) 相同或更短(<=h[i]<= h[i]<=h[i]);
底邊寬度:相比 S(i,j)S(i, j)S(i,j) 更短。
因此所有消去的狀態的面積都 <S(i,j)< S(i, j)<S(i,j)。通俗的講,我們每次向內移動短板,所有的消去狀態都不會導致丟失面積最大值 。
代碼JavaScript 實現/** * @來源: Javascript中文網 - 前端進階資源教程 https://www.javascriptc.com/ * @介紹:前端中文網是以前端進階資源教程分享為主的專業網站,包括:前端、大廠面試題、typescript教程、程序人生、React.js * @param {number[]} height * @return {number} */ var maxArea = function (height) { if (!height || height.length <= 1) return 0; let leftPos = 0; let rightPos = height.length - 1; let max = 0; while (leftPos < rightPos) { const currentArea = Math.abs(leftPos - rightPos) * Math.min(height[leftPos], height[rightPos]); if (currentArea > max) { max = currentArea; } // 更新小的 if (height[leftPos] < height[rightPos]) { leftPos++; } else { // 如果相等就隨便了 rightPos--; } } return max; };/** 作者:li-zhui-zhui 連結:https://leetcode-cn.com/problems/container-with-most-water/solution/11-sheng-zui-duo-shui-de-rong-qi-by-li-zhui-zhui/ * @param {number[]} height * @return {number} */ var maxArea = function (height) { let res = 0, i = 0, j = height.length - 1, cur = 0; while (i < j) { let h = height[i] < height[j] ? height[i] : height[j]; cur = h * (j - i); res = cur > res ? cur : res; if (height[i] < height[j]) { i++; } else { j--; } } return res; }; C++ 實現class Solution { public: int maxArea(vector<int>& height) { auto ret = 0ul, leftPos = 0ul, rightPos = height.size() - 1; while( leftPos < rightPos) { ret = std::max(ret, std::min(height[leftPos], height[rightPos]) * (rightPos - leftPos)); if (height[leftPos] < height[rightPos]) ++leftPos; else --rightPos; } return ret; } };/* 作者:nettee 連結:https://leetcode-cn.com/problems/container-with-most-water/solution/on-shuang-zhi-zhen-jie-fa-li-jie-zheng-que-xing-tu/ */ int maxArea(vector<int>& height) { int res = 0; int i = 0; int j = height.size() - 1; while (i < j) { int area = (j - i) * min(height[i], height[j]); res = max(res, area); if (height[i] < height[j]) { i++; } else { j--; } } return res; } Python 實現class Solution: def maxArea(self, heights): l, r = 0, len(heights) - 1 ans = 0 while l < r: ans = max(ans, (r - l) * min(heights[l], heights[r])) if heights[r] > heights[l]: l += 1 else: r -= 1 return ans# 作者:jyd # 連結:https://leetcode-cn.com/problems/container-with-most-water/solution/container-with-most-water-shuang-zhi-zhen-fa-yi-do/ class Solution: def maxArea(self, height: List[int]) -> int: i, j, res = 0, len(height) - 1, 0 while i < j: if height[i] < height[j]: res = max(res, height[i] * (j - i)) i += 1 else: res = max(res, height[j] * (j - i)) j -= 1 return res# 作者:lxj618 # 連結:https://leetcode-cn.com/problems/container-with-most-water/solution/shuang-zhi-zhen-bian-li-by-lxj618/ class Solution: def maxArea(self, height: List[int]) -> int: # 長度不斷縮短,縮短的過程中只縮短值小的一邊 ans = 0 idx_1, idx_2 = 0, len(height)-1 while idx_1 < idx_2: ans = max(ans, (idx_2-idx_1)*min(height[idx_1],height[idx_2])) if height[idx_1] < height[idx_2]: idx_1 += 1 else: idx_2 -= 1 return ans 其他原題leetcode連結:11.container-with-most-water - https://leetcode-cn.com/problems/container-with-most-water/description/合作方:JavaScript中文網 – 全球極客摯愛的技術成長平臺 說明:leetcode 題解 | 每日一題🔥 所有題目並非全部為本人解答,部分為在複習學習中整理提取其他解題作者的優秀筆記,便於大家學習共同進步,如有侵權,請聯繫刪除。