Description

給定整數陣列 height,其中 height[i] 代表第 i 條線的高度。找出兩條線與 x 軸構成的容器,使其能容納最多的水。回傳最大水量。

Example:

Input: height = [1,8,6,2,5,4,8,3,7] Output: 49

Intuition#

核心思路:雙指針從兩端開始,每次移動較矮的那一側,因為移動較高的一側不可能得到更大面積。

  • 面積 = min(height[left], height[right]) * (right - left)
  • 寬度隨指針靠近一定縮小,所以唯一能讓面積增大的方式是提升高度
  • 移動較矮的指針才「有機會」遇到更高的線;移動較高的指針只會讓高度不變或更低
  • 這是一個貪心策略:每步都捨棄不可能產生更大面積的選擇

Approaches#

1: Brute Force#

  • 概念: 列舉所有 (i, j) 配對,計算面積取最大值
  • 時間複雜度: O(n^2) - 兩層迴圈
  • 空間複雜度: O(1)
class Solution {
    fun maxArea(height: IntArray): Int {
        var maxWater = 0

        for (i in height.indices) {
            for (j in i + 1 until height.size) {
                val water = minOf(height[i], height[j]) * (j - i)
                maxWater = maxOf(maxWater, water)
            }
        }

        return maxWater
    }
}

⭐2: Two Pointers (Greedy)#

  • 概念: 左右指針從兩端開始,計算面積後移動較矮的一側,直到指針相遇
  • 時間複雜度: O(n) - 每步移動一個指針,共 n-1 步
  • 空間複雜度: O(1) - 只用兩個指針和一個最大值變數
class Solution {
    fun maxArea(height: IntArray): Int {
        var left = 0
        var right = height.size - 1
        var maxWater = 0

        while (left < right) {
            val water = minOf(height[left], height[right]) * (right - left)
            maxWater = maxOf(maxWater, water)

            if (height[left] < height[right]) {
                left++
            } else {
                right--
            }
        }

        return maxWater
    }
}

height[left] == height[right] 時,移動任一側皆可(不會漏掉最佳解)。因為若最佳解需要其中一側不動,另一側在內部一定會被遍歷到。

🔑 Takeaways#

  • Pattern: 相向雙指針 + 貪心,適用於「從兩端收縮、寬度遞減」類型的最佳化問題
  • 關鍵技巧: 「移動較矮一側」的貪心證明——移動較高一側,高度受限於較矮方,寬度又縮小,面積必定不增