From 4684b01515d08800c2056b0d4f6f1d6b5fba93d3 Mon Sep 17 00:00:00 2001 From: tom4649 Date: Mon, 1 Jun 2026 06:46:59 +0900 Subject: [PATCH 1/2] 42. Trapping Rain Water --- 0042.Trapping-Rain-Water/memo.md | 86 +++++++++++++++++++ 0042.Trapping-Rain-Water/step1_list.py | 16 ++++ .../step1_list_revised.py | 16 ++++ 0042.Trapping-Rain-Water/step2_two_pointer.py | 20 +++++ .../step2_two_pointer2.py | 20 +++++ 0042.Trapping-Rain-Water/step3_stack.py | 20 +++++ 6 files changed, 178 insertions(+) create mode 100644 0042.Trapping-Rain-Water/memo.md create mode 100644 0042.Trapping-Rain-Water/step1_list.py create mode 100644 0042.Trapping-Rain-Water/step1_list_revised.py create mode 100644 0042.Trapping-Rain-Water/step2_two_pointer.py create mode 100644 0042.Trapping-Rain-Water/step2_two_pointer2.py create mode 100644 0042.Trapping-Rain-Water/step3_stack.py diff --git a/0042.Trapping-Rain-Water/memo.md b/0042.Trapping-Rain-Water/memo.md new file mode 100644 index 0000000..f352df5 --- /dev/null +++ b/0042.Trapping-Rain-Water/memo.md @@ -0,0 +1,86 @@ +# 42. Trapping Rain Water + +## step1 + +思考の流れ: + +問題文の理解 -> 紙に書く -> 左右を入れ替えても同じことに気づく -> 水の高さが min(max_from_left[i], max_from_right[i]) であることに気づく -> 実装 + +全体で 24m ほど。問題文の理解にも時間がかかる問題だった。落ち着いて考察できたから解けたが、面接の状況では同じことができないかもしれない。 + +時間計算量 O(n), 空間計算量 O(n) + +itertools.accumulateにreversedを適用してしまい、`TypeError: 'itertools.accumulate' object is not reversible`が発生した。 + +--- + +## step2 + +LLMに面接官役をやらせてみる + +> Alright, that’s a solid start! Your logic is perfectly correct. You recognized that the water trapped at any given bar depends on the maximum height to its left and right, which shows you understand the core mechanics of the problem. +> Using itertools.accumulate is a clever way to build the prefix and suffix maximums quickly. However, looking at this implementation, I have a few concerns regarding its efficiency and how we are managing memory. +> Let's dig a bit deeper into this. I have a few questions for you. + +1. +> Your current solution runs in $O(N)$ time, which is great. But what is the space complexity here? +> The Follow-up: "Can you optimize this to $O(1)$ auxiliary space? Imagine the input array has millions of elements; allocating all these extra lists will heavily tax the memory. Hint: Think about how we can use pointers to calculate this on the fly." + +2. +> The Issue: You are reversing the list, accumulating it, converting it to a list, and then reversing it again via slicing [::-1]. +> The Follow-up: "While it works, this involves multiple passes over the data just to set up the right-side maximums. It feels a bit clunky. Is there a way we can find the trapped water without needing to pre-compute and store the entire right-side history?" + +3. +> The Issue: You calculated the differences first, stored them in lists, and then zipped them again. You could have just zipped max_from_left and max_from_right directly to find the bottleneck height. +> The Follow-up: "Mathematically, the water at index i is just min(max_left[i], max_right[i]) - height[i]. Why did we create two separate intermediate lists for the differences? Could we compress this logic?" + +1, 3を受けて改善した。順方向のmaxはiteratorなのでメモリを消費しないが、逆方向はlistに変換するせいでO(n)のメモリを避けられない。 + +2でtwo pointerというキーワードが出た。解いているときに空間計算量 O(n) はかかってしまうだろうと考えて書いていたが、O(1)にできるらしい。 + +左と右のポインタのうち、小さいことが確定した時点でポインタを進められることに気がつきtwo pointerの解を得た。20mほどかかった。 + +過去に似た問題を解いた気がしたので、確認してみると以下の問題だった。 + +https://leetcode.com/problems/container-with-most-water/description/?envType=problem-list-v2&envId=rab78cw1 + +## 他の人のコード + +https://github.com/shining-ai/leetcode/pull/63 + +スタックを使った解法は思いつかなかった。 + +```python +class Solution: + def trap(self, height: List[int]) -> int: + left_walls = [] + total_water = 0 + for right in range(len(height)): + while left_walls and height[left_walls[-1]] <= height[right]: + bottom_index = left_walls.pop() + if not left_walls: + break + left = left_walls[-1] + distance = right - left - 1 + depth = min(height[left], height[right]) - height[bottom_index] + total_water += distance * depth + left_walls.append(right) + return total_water +``` + +> max_left ですと、最大の左を表しているように感じられます。 max_heights_in_left あたりはいかがでしょうか? + +listであることがわかりやすいので、step1_revisedに反映する。max_heights_from_leftとした + +https://github.com/Yuto729/leetcode/pull/69/changes + +> Two Pointer 系の整合したコードの書き方 + +step2_two_pointerは (l, r) を未処理範囲としている。 + +step2_two_pointer2として [l, r]を未処理範囲としたものも書く。 + + +## step3 + +最も馴染みの薄いstackを用いた解法を書く。 diff --git a/0042.Trapping-Rain-Water/step1_list.py b/0042.Trapping-Rain-Water/step1_list.py new file mode 100644 index 0000000..2bd4e69 --- /dev/null +++ b/0042.Trapping-Rain-Water/step1_list.py @@ -0,0 +1,16 @@ +import itertools + + +class Solution: + def trap(self, height: list[int]) -> int: + max_from_left = itertools.accumulate(height, max) + max_from_right = reversed(list(itertools.accumulate(reversed(height), max))) + + difference_max_left = [m - h for m, h in zip(max_from_left, height)] + difference_max_right = [m - h for m, h in zip(max_from_right, height)] + + water_trapped = 0 + for left, right in zip(difference_max_left, difference_max_right): + water_trapped += min(left, right) + + return water_trapped diff --git a/0042.Trapping-Rain-Water/step1_list_revised.py b/0042.Trapping-Rain-Water/step1_list_revised.py new file mode 100644 index 0000000..d08cfc2 --- /dev/null +++ b/0042.Trapping-Rain-Water/step1_list_revised.py @@ -0,0 +1,16 @@ +import itertools + + +class Solution: + def trap(self, height: list[int]) -> int: + max_heights_from_left = itertools.accumulate(height, max) + max_heights_from_right = reversed( + list(itertools.accumulate(reversed(height), max)) + ) + + return sum( + min(m_left, m_right) - h + for m_left, m_right, h in zip( + max_heights_from_left, max_heights_from_right, height + ) + ) diff --git a/0042.Trapping-Rain-Water/step2_two_pointer.py b/0042.Trapping-Rain-Water/step2_two_pointer.py new file mode 100644 index 0000000..dc9bbd5 --- /dev/null +++ b/0042.Trapping-Rain-Water/step2_two_pointer.py @@ -0,0 +1,20 @@ +class Solution: + def trap(self, height: list[int]) -> int: + index_left = 0 + index_right = len(height) - 1 + + max_from_left = height[index_left] + max_from_right = height[index_right] + + water_trapped = 0 + while index_left < index_right: + if max_from_left <= max_from_right: + water_trapped += max_from_left - height[index_left] + index_left += 1 + max_from_left = max(max_from_left, height[index_left]) + else: + water_trapped += max_from_right - height[index_right] + index_right -= 1 + max_from_right = max(max_from_right, height[index_right]) + + return water_trapped diff --git a/0042.Trapping-Rain-Water/step2_two_pointer2.py b/0042.Trapping-Rain-Water/step2_two_pointer2.py new file mode 100644 index 0000000..26289d4 --- /dev/null +++ b/0042.Trapping-Rain-Water/step2_two_pointer2.py @@ -0,0 +1,20 @@ +class Solution: + def trap(self, height: list[int]) -> int: + index_left = 0 + index_right = len(height) - 1 + + max_from_left = 0 + max_from_right = 0 + + water_trapped = 0 + while index_left <= index_right: + if max_from_left <= max_from_right: + water_trapped += max(max_from_left - height[index_left], 0) + max_from_left = max(max_from_left, height[index_left]) + index_left += 1 + else: + water_trapped += max(max_from_right - height[index_right], 0) + max_from_right = max(max_from_right, height[index_right]) + index_right -= 1 + + return water_trapped diff --git a/0042.Trapping-Rain-Water/step3_stack.py b/0042.Trapping-Rain-Water/step3_stack.py new file mode 100644 index 0000000..a8ce59e --- /dev/null +++ b/0042.Trapping-Rain-Water/step3_stack.py @@ -0,0 +1,20 @@ +class Solution: + def trap(self, height: list[int]) -> int: + indices_of_decreasing_height = [] + water_trapped = 0 + + for i_right, h_right in enumerate(height): + while ( + indices_of_decreasing_height + and height[indices_of_decreasing_height[-1]] < h_right + ): + bottom = indices_of_decreasing_height.pop() + if not indices_of_decreasing_height: + break + i_left = indices_of_decreasing_height[-1] + width = i_right - i_left - 1 + bounded_height = min(height[i_left], h_right) - height[bottom] + water_trapped += width * bounded_height + indices_of_decreasing_height.append(i_right) + + return water_trapped From 4676fea92233d99eda1fe940faf67c2f5b9e7677 Mon Sep 17 00:00:00 2001 From: tom4649 Date: Tue, 2 Jun 2026 18:14:24 +0900 Subject: [PATCH 2/2] Rename variables --- 0042.Trapping-Rain-Water/step1_list.py | 6 +++--- 0042.Trapping-Rain-Water/step2_two_pointer.py | 8 ++++---- 0042.Trapping-Rain-Water/step2_two_pointer2.py | 8 ++++---- 0042.Trapping-Rain-Water/step3_stack.py | 18 +++++++++--------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/0042.Trapping-Rain-Water/step1_list.py b/0042.Trapping-Rain-Water/step1_list.py index 2bd4e69..e525369 100644 --- a/0042.Trapping-Rain-Water/step1_list.py +++ b/0042.Trapping-Rain-Water/step1_list.py @@ -9,8 +9,8 @@ def trap(self, height: list[int]) -> int: difference_max_left = [m - h for m, h in zip(max_from_left, height)] difference_max_right = [m - h for m, h in zip(max_from_right, height)] - water_trapped = 0 + trapped_water = 0 for left, right in zip(difference_max_left, difference_max_right): - water_trapped += min(left, right) + trapped_water += min(left, right) - return water_trapped + return trapped_water diff --git a/0042.Trapping-Rain-Water/step2_two_pointer.py b/0042.Trapping-Rain-Water/step2_two_pointer.py index dc9bbd5..55c300f 100644 --- a/0042.Trapping-Rain-Water/step2_two_pointer.py +++ b/0042.Trapping-Rain-Water/step2_two_pointer.py @@ -6,15 +6,15 @@ def trap(self, height: list[int]) -> int: max_from_left = height[index_left] max_from_right = height[index_right] - water_trapped = 0 + trapped_water = 0 while index_left < index_right: if max_from_left <= max_from_right: - water_trapped += max_from_left - height[index_left] + trapped_water += max_from_left - height[index_left] index_left += 1 max_from_left = max(max_from_left, height[index_left]) else: - water_trapped += max_from_right - height[index_right] + trapped_water += max_from_right - height[index_right] index_right -= 1 max_from_right = max(max_from_right, height[index_right]) - return water_trapped + return trapped_water diff --git a/0042.Trapping-Rain-Water/step2_two_pointer2.py b/0042.Trapping-Rain-Water/step2_two_pointer2.py index 26289d4..febac00 100644 --- a/0042.Trapping-Rain-Water/step2_two_pointer2.py +++ b/0042.Trapping-Rain-Water/step2_two_pointer2.py @@ -6,15 +6,15 @@ def trap(self, height: list[int]) -> int: max_from_left = 0 max_from_right = 0 - water_trapped = 0 + trapped_water = 0 while index_left <= index_right: if max_from_left <= max_from_right: - water_trapped += max(max_from_left - height[index_left], 0) + trapped_water += max(max_from_left - height[index_left], 0) max_from_left = max(max_from_left, height[index_left]) index_left += 1 else: - water_trapped += max(max_from_right - height[index_right], 0) + trapped_water += max(max_from_right - height[index_right], 0) max_from_right = max(max_from_right, height[index_right]) index_right -= 1 - return water_trapped + return trapped_water diff --git a/0042.Trapping-Rain-Water/step3_stack.py b/0042.Trapping-Rain-Water/step3_stack.py index a8ce59e..91ccdd2 100644 --- a/0042.Trapping-Rain-Water/step3_stack.py +++ b/0042.Trapping-Rain-Water/step3_stack.py @@ -1,20 +1,20 @@ class Solution: def trap(self, height: list[int]) -> int: indices_of_decreasing_height = [] - water_trapped = 0 + trapped_water = 0 - for i_right, h_right in enumerate(height): + for index_right, height_right in enumerate(height): while ( indices_of_decreasing_height - and height[indices_of_decreasing_height[-1]] < h_right + and height[indices_of_decreasing_height[-1]] < height_right ): bottom = indices_of_decreasing_height.pop() if not indices_of_decreasing_height: break - i_left = indices_of_decreasing_height[-1] - width = i_right - i_left - 1 - bounded_height = min(height[i_left], h_right) - height[bottom] - water_trapped += width * bounded_height - indices_of_decreasing_height.append(i_right) + index_left = indices_of_decreasing_height[-1] + width = index_right - index_left - 1 + bounded_height = min(height[index_left], height_right) - height[bottom] + trapped_water += width * bounded_height + indices_of_decreasing_height.append(index_right) - return water_trapped + return trapped_water