---
# System prepended metadata

title: 【LeetCode刷題】【演算法學習筆記】接水桶
tags: [靈神基礎算法]

---

# 【LeetCode刷題】【演算法學習筆記】相向雙指針 接水桶
### 11. Container With Most Water
✎ 題目連結：[Leetcode](https://leetcode.com/problems/container-with-most-water/description/) / [力扣](https://leetcode.cn/problems/container-with-most-water/description/)

:::spoiler 💡思路
1. reference:  [灵茶山艾府](https://leetcode.cn/problems/container-with-most-water/solutions/1974337/by-endlesscheng-f0xz/)
2. 說明: 目標是找出兩條垂直線組成的最大容器，使容器內的水最多。
3. 思路: 我們有一組數字 ==height==，代表不同位置的垂直線長度，每條線的 x 座標對應索引位置，而 y 座標則是 ==height[i]==。我們要找出兩條線，使它們與 x 軸組成的長方形面積最大。
我們這裡使用 雙指針法 **(Two Pointers)**，透過 **「從兩端向內移動」** 的方式，來找到最大的容器。
:::
```python=
class Solution:
    def maxArea(self, height: List[int]) -> int: # height 為高度
        # 初始化指針
        ans = left = 0 # 用來存儲目前找到的最大面積
        right = len(height) - 1

        while left < right:
            # 計算當前面積
            area = (right - left) * min(height[left], height[right])
            # 判斷當前最大面積
            ans = max(ans, area)

            # 移動指針
            # 誰短就移動誰
            # 因為總是移動 較短的那一邊，才有機會找到更大的面積
            if height[left] < height[right]:
                left += 1
            else:
                right -= 1
                
        # 指針相遇，沒有區間可以形成容器，迴圈終止
        # ans 已經存下了過程中找到的最大面積
        return ans
```


### 42. Trapping Rain Water
---
✎ 題目連結：[Leetcode](https://leetcode.com/problems/trapping-rain-water/description/) / [力扣](https://leetcode.cn/problems/trapping-rain-water/description/)

:::spoiler 💡思路
1. reference:  [灵茶山艾府](https://leetcode.cn/problems/trapping-rain-water/solutions/1974340/zuo-liao-nbian-huan-bu-hui-yi-ge-shi-pin-ukwm/)
2. 說明: 題目希望我們創建 程式碼目的是 **計算積水量**，也就是找出雨水可以存在哪些地方，並計算總共可以存多少水。目的是解決 接雨水（Trapping Rain Water） 的問題，也就是給定一個高度陣列 ==height==，求出凹槽中最多能接多少水。
3. 思路: 水的存量取決於 左邊最高的柱子 和 右邊最高的柱子 之間的最小值，減去當前柱子的高度。
:::
#### 寫法一: 前後綴分解
```python=
from typing import List

class Solution:
    def trap(self, height: List[int]) -> int:
        n = len(height) # 柱子數

        preMax = [0] * n # preMax 記錄從最左邊數過來最大高度
        preMax[0] = height[0]
        # 拜訪最大高度
        for i in range(1, n):
            preMax[i] = max(preMax[i - 1], height[i]) # 確保 pre_max[i] 是前面最大柱子的高度

        # 計算每個位置的右邊最大值
        sufMax = [0] * n 
        sufMax[n-1] = height[n-1]
        for i in range(n-2, -1, -1): 
            sufMax[i] = max(sufMax[i + 1], height[i])

        # 計算每個位置能接的水量
        ans = 0
        for h, pre, suf in zip(height, preMax, sufMax): # 透過 zip() 來同時來拜訪三個參數
            ans += min(pre, suf) - h
        return ans
```

#### 寫法二: 相向雙指針
:::spoiler 💡思路
在一個地形 height 中，如果要存水，則該位置的水量取決於：

* 左邊最高的柱子
* 右邊最高的柱子
* 當前的柱子高度

水量計算公式：
    水量 = **min(左邊最高柱子, 右邊最高柱子)−當前柱子高度**
    
我們使用 雙指針，從兩邊向中間移動，**動態計算水量**。
:::

```python=
from typing import List

class Solution:
    def trap(self, height: List[int]) -> int:
        n = len(height) # 柱子數
        ans = left = preMax = sufMax = 0 # 變數初始化
        right = n - 1

        # 使用雙指針來計算水量
        while left < right:
            # 計算當前左右邊最高的柱子高度
            preMax = max(preMax, height[left])
            sufMax = max(sufMax, height[right])

            # 誰小誰先移動 因為最高的柱子無法接水 設為相遇位置
            # 水位由兩邊最高柱子相對低決定
            if preMax < sufMax: # 左柱較低
                ans += preMax - height[left]
                left += 1
            else:
                ans += sufMax - height[right]
                right -= 1
        return ans
```

#### 寫法三: 相向雙指針
:::spoiler 💡思路
* 這段程式碼的核心概念是「木桶效應」：某個位置能存多少水，取決於它左右兩側的最高柱子中較低的那個。
* 我們用 leftMax 和 rightMax 記錄當前遇到的最高柱子，確保我們能正確計算積水量。
:::

```python=
from typing import List

class Solution:
    def trap(self, height: List[int]) -> int:
        # 排除空柱子
        if not height:
            return 0

        # 初始化變數
        left, right = 0, len(height) - 1
        leftMax, rightMax = 0, 0
        waterTrapped = 0

        # 使用雙指針計算水量
        while left < right:
            # 如果右柱較高
            if height[left] < height[right]:
                # 較低的柱子 左柱決定水容量 計算加水量
                if height[left] >= leftMax: # 當前柱子大於等於左柱最高 不加水並更新左柱最高高度
                    leftMax = height[left]
                else: 
                    waterTrapped += leftMax - height[left]
                left += 1
            # 左柱較高 
            else:
                # 右柱當前高度 大於等於當前右柱最高
                if height[right] >= rightMax: # 更新右柱最高高度
                    rightMax = height[right]
                else: 
                    waterTrapped += rightMax - height[right] # 水漥低於水平面 可以容納水
                right -= 1

        return waterTrapped
```
:::spoiler **寫法二 v.s. 寫法三 效能比較**
**Code 1 比 Code 2 更快，原因如下：**

### **1. 變數更新次數**
- **Code 1:**  
  - 只更新 `preMax` 和 `sufMax` **一次**（每次迴圈只更新一次）。
- **Code 2:**  
  - `leftMax` 和 `rightMax` 在某些情況下會 **被重複賦值**（在 `if` 判斷中會多次賦值）。

### **2. 判斷條件更簡潔**
- **Code 1:**  
  - 直接比較 `preMax` 和 `sufMax`，避免了 `if height[left] < height[right]` 這種額外的條件判斷。
- **Code 2:**  
  - 內部有兩層 `if-else`，增加了額外的邏輯判斷，導致運算稍微變慢。

### **3. 指標移動效率**
- **Code 1:**  
  - 指標 `left` 和 `right` 只根據 `preMax < sufMax` 來移動，簡單且高效。
- **Code 2:**  
  - `left` 和 `right` 的移動依賴於 `height[left] < height[right]`，內部還有 `if height[left] >= leftMax` 的判斷，導致運算稍微變慢。

### **4. 總體比較**
| **比較項目** | **Code 1** | **Code 2** |
|------------|----------|----------|
| 變數更新次數 | 少 | 多 |
| 邏輯判斷次數 | 少 | 多 |
| 指標移動次數 | 少 | 稍多 |
| 效率 | 更快 | 稍慢 |

### **結論**
- **Code 1** 比 **Code 2** **更快**，因為：
  - 變數更新次數較少。
  - 判斷邏輯較少，減少 `if-else` 開銷。
  - 指標移動規則更簡單，避免不必要的計算。

這使得 **Code 1 在實際執行時會略快於 Code 2** 🚀
:::