[LeetCode解題攻略] 42. Trapping Rain Water

更新於 發佈於 閱讀時間約 7 分鐘

題目描述

給定一個整數數組 height,每個元素表示一個柱子的高度,其中每個柱子的寬度為 1,請計算柱子之間可以容納多少雨水。


範例

範例 1

輸入: height = [0,1,0,2,1,0,1,3,2,1,2,1]
輸出: 6
raw-image


範例 2

輸入: height = [4,2,0,3,2,5]
輸出: 9

解題思路

這是一個經典的動態規劃與雙指針問題。

  1. 直觀思路
    雨水能夠存儲的量由兩側的最高柱子決定,對於每個柱子,我們需要知道:
    • 它左側最高柱子的高度。
    • 它右側最高柱子的高度。
    • 存水量 = min(左側最高高度, 右側最高高度) - 當前高度。
  2. 優化
    • 可以使用兩種方法:動態規劃 或 雙指針法。

解法一:暴力解法

核心思想

對於每個柱子,找到其左側和右側的最大高度,計算可以容納的雨水量。


程式碼

class Solution(object):
def trap(self, height):
"""
:type height: List[int]
:rtype: int
"""
n = len(height)
if n == 0:
return 0

water_trapped = 0
for i in range(n):
# 找到左側最大高度
left_max = max(height[:i+1])
# 找到右側最大高度
right_max = max(height[i:])
# 計算雨水量
water_trapped += min(left_max, right_max) - height[i]

return water_trapped

時間與空間複雜度

  • 時間複雜度:O(n^2)
    • 對於每個柱子,我們需要遍歷一次找左、右最大高度。
  • 空間複雜度:O(1)
    • 不需要額外的儲存空間。

解法二:動態規劃

核心思想

用兩個數組 left_maxright_max 分別保存每個柱子的左側和右側最大高度,避免暴力解法中重複計算。


步驟

  1. 建立兩個數組:
    • left_max[i] 表示 i 左側的最大高度(包含自己)。
    • right_max[i] 表示 i 右側的最大高度(包含自己)。
  2. 遍歷柱子,計算每個位置的存水量。

程式碼

class Solution(object):
def trap(self, height):
n = len(height)
if n == 0:
return 0

# 預處理左側和右側最大高度
left_max = [0] * n
right_max = [0] * n

left_max[0] = height[0]
for i in range(1, n):
left_max[i] = max(left_max[i-1], height[i])

right_max[n-1] = height[n-1]
for i in range(n-2, -1, -1):
right_max[i] = max(right_max[i+1], height[i])

# 計算存水量
water_trapped = 0
for i in range(n):
water_trapped += min(left_max[i], right_max[i]) - height[i]

return water_trapped

時間與空間複雜度

  • 時間複雜度:O(n)
    • 需要兩次遍歷計算 left_max 和 right_max,以及一次遍歷計算雨水量。
  • 空間複雜度:O(n)
    • 額外使用了 left_max 和 right_max 數組。

解法三:雙指針法

核心思想

使用兩個指針 leftright 來替代 left_maxright_max 數組,只需維護當前的左右最大高度即可。


步驟

  1. 使用雙指針 leftright,分別指向數組的開頭和結尾。
  2. 維護左右最大高度 left_maxright_max
  3. 如果左側最大高度小於右側最大高度:
    • 判斷當前柱子是否可以存水:left_max - height[left]。
    • 然後移動 left 指針。
  4. 如果右側最大高度小於左側最大高度:
    • 判斷當前柱子是否可以存水:right_max - height[right]。
    • 然後移動 right 指針。

程式碼

class Solution(object):
def trap(self, height):
n = len(height)
if n == 0:
return 0

left, right = 0, n - 1
left_max, right_max = 0, 0
water_trapped = 0

while left < right:
if height[left] < height[right]:
if height[left] >= left_max:
left_max = height[left]
else:
water_trapped += left_max - height[left]
left += 1
else:
if height[right] >= right_max:
right_max = height[right]
else:
water_trapped += right_max - height[right]
right -= 1

return water_trapped

時間與空間複雜度

  • 時間複雜度:O(n)
    • 雙指針從頭到尾只遍歷一次數組。
  • 空間複雜度:O(1)
    • 只使用了常數級別的額外變量。

結論

  1. 雙指針法 是最優解,時間與空間複雜度均表現優秀。
  2. 動態規劃法 提供了清晰的分步邏輯,適合學習初期使用。
  3. 暴力解法 雖然直觀,但不推薦在大型數組中使用。

你可以根據自己的熟悉程度選擇適合的解法!

歡迎來到我的部落格!這裡記錄了軟體工程師的日常生活點滴,並分享程式設計與演算法的實用教學。無論你是初學者還是有經驗的開發者,都能在這裡找到深入淺出的技術解析與實戰技巧。此外,我也會分享工作中的心路歷程與學習心得,讓你不僅學到技術,更能瞭解軟體開發的實際應用與挑戰。希望透過這個平台,能與你共同成長,激發對技術的熱情!
留言
avatar-img
留言分享你的想法!

































































給定一個未排序的整數數組 nums,找出其中的最小的缺失的正整數。 必須設計一個時間複雜度為 O(n) 的解法,並且空間複雜度為 O(1) 。
給定一個可能包含重複數字的整數數組 candidates 和一個目標值 target,找出所有的唯一組合,使得這些組合中的數字和為 target。 數組中的每個數字只能在每個組合中使用一次。
給定一個無重複正整數數組 candidates 和一個目標值 target,找出所有可以使數字和為 target 的組合。 數組中的數字可以無限制重複選取,並且所有的組合需要是唯一的。
Count-and-say序列是透過遞歸公式定義的數字串序列: countAndSay(1) = "1" countAndSay(n) 是 countAndSay(n - 1) 的Run-length encoding。
編寫一個程式,求解數獨(Sudoku)問題。
給定一個 9x9 的數獨棋盤,請驗證這個棋盤是否有效。
給定一個未排序的整數數組 nums,找出其中的最小的缺失的正整數。 必須設計一個時間複雜度為 O(n) 的解法,並且空間複雜度為 O(1) 。
給定一個可能包含重複數字的整數數組 candidates 和一個目標值 target,找出所有的唯一組合,使得這些組合中的數字和為 target。 數組中的每個數字只能在每個組合中使用一次。
給定一個無重複正整數數組 candidates 和一個目標值 target,找出所有可以使數字和為 target 的組合。 數組中的數字可以無限制重複選取,並且所有的組合需要是唯一的。
Count-and-say序列是透過遞歸公式定義的數字串序列: countAndSay(1) = "1" countAndSay(n) 是 countAndSay(n - 1) 的Run-length encoding。
編寫一個程式,求解數獨(Sudoku)問題。
給定一個 9x9 的數獨棋盤,請驗證這個棋盤是否有效。
你可能也想看
Google News 追蹤
Thumbnail
題目敘述 Patching Array 題目給定一個整數陣列, 請問還要補上多少個數字,才能用這些數字的和拼湊出所有1~n的整數。
Thumbnail
Leetcode 精選75題 題目與題解 熱門考點 目錄 (持續更新中) 建議從左側目錄 或者 按Ctrl+F輸入關鍵字進行搜尋
Thumbnail
題目給定一個布林代數的二元樹,要求我們計算最後的結果。 葉子節點都是真假值 非葉子節點都是布林運算子
Thumbnail
輸入給定一個已經從小到大排序好,而且彼此互質的整數陣列, 請問任取兩數分別當作分子、分母,第k小的分數是多少? 輸出請以 [分子,分母] 的形式回傳答案。
Thumbnail
題目敘述 題目會給定一棵二元樹的根結點, 要求我們計算滿足局部路徑節點和=targetSum的數目有多少? 註: 局部路徑節點和 =由節點a往下走到某個節點b,這個區間內的節點值總和 題目的原文敘述 測試範例 Example 1: Input: root = [10,5,-3,3
Thumbnail
題目敘述 題目會給定我們一格花盆陣列flowerbed,和欲種植的花朵數目n。 花盆陣列中,0代表空位,1代表已經有種好的花盆存在。 種花的要求是,不能有兩兩相鄰的花盆出現,中間一定要間隔至少一個空位。 問我們在給定的條件下,能不能順利種完n個花朵盆栽? 若可以返回True,若無解返回Fa
Thumbnail
題目會給定一個輸入陣列temperatures ,分別代表每一天的溫度。 請計算每一天還要再過幾天才會遇到更溫暖的日子,如果遇不到,則回填0。 請以陣列的形式返回答案。 題目的原文敘述 約束條件 Constraints: 1 <= temperatures.length <= 10^
Thumbnail
題目敘述 題目會給我們一個輸入陣列nums,每個元素值代表那個格子點可以跳躍的最大長度。 一開始從最左邊的格子點出發開始跳,請問可以成功抵達終點,也就是最右邊的格子點嗎? 如果可以,返回 True。 如果不行,返回False。 題目的原文敘述 測試範例 Example 1: In
Thumbnail
題目敘述 題目會給我們一個隔板陣列height,代表每個隔板的高度,讓我們選取兩個隔板作為水槽的邊界,請問最多我們能裝多少水? 題目的原文敘述 測試範例 Example 1: Input: height = [1,8,6,2,5,4,8,3,7] Output: 49 Explanati
Thumbnail
題目會給我們一個陣列,分別代表高低各不同的隔板高度,問我們從降雨之後,最多可以儲存多少水量?
Thumbnail
題目敘述 Patching Array 題目給定一個整數陣列, 請問還要補上多少個數字,才能用這些數字的和拼湊出所有1~n的整數。
Thumbnail
Leetcode 精選75題 題目與題解 熱門考點 目錄 (持續更新中) 建議從左側目錄 或者 按Ctrl+F輸入關鍵字進行搜尋
Thumbnail
題目給定一個布林代數的二元樹,要求我們計算最後的結果。 葉子節點都是真假值 非葉子節點都是布林運算子
Thumbnail
輸入給定一個已經從小到大排序好,而且彼此互質的整數陣列, 請問任取兩數分別當作分子、分母,第k小的分數是多少? 輸出請以 [分子,分母] 的形式回傳答案。
Thumbnail
題目敘述 題目會給定一棵二元樹的根結點, 要求我們計算滿足局部路徑節點和=targetSum的數目有多少? 註: 局部路徑節點和 =由節點a往下走到某個節點b,這個區間內的節點值總和 題目的原文敘述 測試範例 Example 1: Input: root = [10,5,-3,3
Thumbnail
題目敘述 題目會給定我們一格花盆陣列flowerbed,和欲種植的花朵數目n。 花盆陣列中,0代表空位,1代表已經有種好的花盆存在。 種花的要求是,不能有兩兩相鄰的花盆出現,中間一定要間隔至少一個空位。 問我們在給定的條件下,能不能順利種完n個花朵盆栽? 若可以返回True,若無解返回Fa
Thumbnail
題目會給定一個輸入陣列temperatures ,分別代表每一天的溫度。 請計算每一天還要再過幾天才會遇到更溫暖的日子,如果遇不到,則回填0。 請以陣列的形式返回答案。 題目的原文敘述 約束條件 Constraints: 1 <= temperatures.length <= 10^
Thumbnail
題目敘述 題目會給我們一個輸入陣列nums,每個元素值代表那個格子點可以跳躍的最大長度。 一開始從最左邊的格子點出發開始跳,請問可以成功抵達終點,也就是最右邊的格子點嗎? 如果可以,返回 True。 如果不行,返回False。 題目的原文敘述 測試範例 Example 1: In
Thumbnail
題目敘述 題目會給我們一個隔板陣列height,代表每個隔板的高度,讓我們選取兩個隔板作為水槽的邊界,請問最多我們能裝多少水? 題目的原文敘述 測試範例 Example 1: Input: height = [1,8,6,2,5,4,8,3,7] Output: 49 Explanati
Thumbnail
題目會給我們一個陣列,分別代表高低各不同的隔板高度,問我們從降雨之後,最多可以儲存多少水量?