From de8341b8d5b63676893a881afcfe77c43df69b72 Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Sun, 31 Aug 2025 14:22:21 +0900 Subject: [PATCH 1/5] Solve 253_meeting_rooms_ii_medium --- 253_meeting_rooms_ii_medium/README.md | 147 ++++++++++++++++++ 253_meeting_rooms_ii_medium/step1.py | 34 ++++ 253_meeting_rooms_ii_medium/step2.py | 33 ++++ 253_meeting_rooms_ii_medium/step3.py | 13 ++ 253_meeting_rooms_ii_medium/timeline_sweep.py | 32 ++++ 253_meeting_rooms_ii_medium/two_pointers.py | 29 ++++ 6 files changed, 288 insertions(+) create mode 100644 253_meeting_rooms_ii_medium/README.md create mode 100644 253_meeting_rooms_ii_medium/step1.py create mode 100644 253_meeting_rooms_ii_medium/step2.py create mode 100644 253_meeting_rooms_ii_medium/step3.py create mode 100644 253_meeting_rooms_ii_medium/timeline_sweep.py create mode 100644 253_meeting_rooms_ii_medium/two_pointers.py diff --git a/253_meeting_rooms_ii_medium/README.md b/253_meeting_rooms_ii_medium/README.md new file mode 100644 index 0000000..25b948f --- /dev/null +++ b/253_meeting_rooms_ii_medium/README.md @@ -0,0 +1,147 @@ +# 問題へのリンク +[253. Meeting Rooms II](https://leetcode.com/problems/meeting-rooms-ii/) + +# 言語 +Python + +# 問題の概要 + +会議室のスケジュールを管理するために、最小限の会議室を確保するとき、いくつの会議室が必要かを求める問題。各会議は開始時刻と終了時刻で表され、すべての会議が重複しないように会議室を割り当てる必要がある。 + +# 自分の解法 + +## step1 + +```python +from heapq import heappop, heappush + + +class Solution: + def minMeetingRooms(self, intervals: list[list[int]]) -> int: + if not intervals: + return 0 + intervals.sort() + start, end = intervals[0] + room_end_times: list[int] = [end] + for i, (start, end) in enumerate(intervals): + if i == 0: + continue + earliest_end = room_end_times[0] + # insert a new interval into existing room if available + if earliest_end <= start: + heappush(room_end_times, end) + heappop(room_end_times) # remove previous endtime + else: + # if not available, then a new room is required + heappush(room_end_times, end) + + return len(room_end_times) +``` + + +- 時間計算量:`O(n log n)` +- 空間計算量:`O(n)` + +## step2 + +```python +from heapq import heappush, heapreplace + + +class Solution: + def minMeetingRooms(self, intervals: list[list[int]]) -> int: + if not intervals: + return 0 + + intervals.sort() + start, end = intervals[0] + meeting_end_times = [] + heappush(meeting_end_times, end) + for start, end in intervals[1:]: + earliest_end = meeting_end_times[0] + # If the earliest available room is free, reuse it. + if earliest_end <= start: + heapreplace(meeting_end_times, end) + # Otherwise, allocate a new room + else: + heappush(meeting_end_times, end) + + return len(meeting_end_times) +``` + +- `heappush`と`heappop`を使用する代わりに`heapreplace`を使用することで、ヒープの操作を効率化&コードが明快に。 + - なぜ効率的か? + - `heappop`はヒープの最小要素を削除したあと、ヒープの最後の要素を根に移動し、ヒープ木を再構築する + - `heappush`も新しい要素を追加したあと、ヒープ木を再構築する + - `heapreplace`は最小要素を削除して新しい要素を追加する操作を1回で行うため、効率的である。 + + +## step3 + +## step4 (FB) + + + +# 別解・模範解答 + +## Timeline Sweep + +```python +class Solution: + def minMeetingRooms(self, intervals: list[list[int]]) -> int: + meeting_events = [] + # meeting events will be sorted in ascending order + # so end must come ahead + START = 1 + END = 0 + for start_time, end_time in intervals: + meeting_events.append((start_time, START)) + meeting_events.append((end_time, END)) + + meeting_events.sort() + + max_rooms = 0 + num_rooms = 0 + for _, event_type in meeting_events: + if event_type == START: + num_rooms += 1 + if event_type == END: + num_rooms -= 1 + max_rooms = max(max_rooms, num_rooms) + return max_rooms +``` + + +- 時間計算量:`O(n log n)` +- 空間計算量:`O(n)` + +## Two Pointers + +```python +class Solution: + def minMeetingRooms(self, intervals: list[list[int]]) -> int: + start_times: list[int] = [interval[0] for interval in intervals] + end_times: list[int] = [interval[1] for interval in intervals] + + start_times.sort() + end_times.sort() + max_rooms = 0 + start_time_pointer = 0 + end_time_pointer = 0 + while start_time_pointer < len(intervals): + start_time = start_times[start_time_pointer] + end_time = end_times[end_time_pointer] + if start_time < end_time: + start_time_pointer += 1 + else: + end_time_pointer += 1 + max_rooms = max(max_rooms, start_time_pointer - end_time_pointer) + return max_rooms +``` + +- LeetCodeに掲載されている模範解答。しかしTimeline Sweepの方がコードが明快で理解しやすいと感じた。 +- 時間計算量:`O(n log n)` +- 空間計算量:`O(n)` + +# 次に解く問題の予告 +- [142. Linked List Cycle II](https://leetcode.com/problems/linked-list-cycle-ii/) diff --git a/253_meeting_rooms_ii_medium/step1.py b/253_meeting_rooms_ii_medium/step1.py new file mode 100644 index 0000000..9a432c9 --- /dev/null +++ b/253_meeting_rooms_ii_medium/step1.py @@ -0,0 +1,34 @@ +# +# @lc app=leetcode id=253 lang=python3 +# +# [253] Meeting Rooms II +# + + +# @lc code=start +from heapq import heappop, heappush + + +class Solution: + def minMeetingRooms(self, intervals: list[list[int]]) -> int: + if not intervals: + return 0 + intervals.sort() + start, end = intervals[0] + room_end_times: list[int] = [end] + for i, (start, end) in enumerate(intervals): + if i == 0: + continue + earliest_end = room_end_times[0] + # insert a new interval into existing room if available + if earliest_end <= start: + heappush(room_end_times, end) + heappop(room_end_times) # remove previous endtime + else: + # if not available, then a new room is required + heappush(room_end_times, end) + + return len(room_end_times) + + +# @lc code=end diff --git a/253_meeting_rooms_ii_medium/step2.py b/253_meeting_rooms_ii_medium/step2.py new file mode 100644 index 0000000..effc745 --- /dev/null +++ b/253_meeting_rooms_ii_medium/step2.py @@ -0,0 +1,33 @@ +# +# @lc app=leetcode id=253 lang=python3 +# +# [253] Meeting Rooms II +# + +# @lc code=start + +from heapq import heappush, heapreplace + + +class Solution: + def minMeetingRooms(self, intervals: list[list[int]]) -> int: + if not intervals: + return 0 + + intervals.sort() + start, end = intervals[0] + meeting_end_times = [] + heappush(meeting_end_times, end) + for start, end in intervals[1:]: + earliest_end = meeting_end_times[0] + # If the earliest available room is free, reuse it. + if earliest_end <= start: + heapreplace(meeting_end_times, end) + # Otherwise, allocate a new room + else: + heappush(meeting_end_times, end) + + return len(meeting_end_times) + + +# @lc code=end diff --git a/253_meeting_rooms_ii_medium/step3.py b/253_meeting_rooms_ii_medium/step3.py new file mode 100644 index 0000000..58b34f3 --- /dev/null +++ b/253_meeting_rooms_ii_medium/step3.py @@ -0,0 +1,13 @@ +# +# @lc app=leetcode id=253 lang=python3 +# +# [253] Meeting Rooms II +# + +# @lc code=start +class Solution: + def minMeetingRooms(self, intervals: list[list[int]]) -> int: + return 0 + + +# @lc code=end diff --git a/253_meeting_rooms_ii_medium/timeline_sweep.py b/253_meeting_rooms_ii_medium/timeline_sweep.py new file mode 100644 index 0000000..4a3a5fc --- /dev/null +++ b/253_meeting_rooms_ii_medium/timeline_sweep.py @@ -0,0 +1,32 @@ +# +# @lc app=leetcode id=253 lang=python3 +# +# [253] Meeting Rooms II +# + +# @lc code=start +class Solution: + def minMeetingRooms(self, intervals: list[list[int]]) -> int: + meeting_events = [] + # meeting events will be sorted in ascending order + # so end must come ahead + START = 1 + END = 0 + for start_time, end_time in intervals: + meeting_events.append((start_time, START)) + meeting_events.append((end_time, END)) + + meeting_events.sort() + + max_rooms = 0 + num_rooms = 0 + for _, event_type in meeting_events: + if event_type == START: + num_rooms += 1 + if event_type == END: + num_rooms -= 1 + max_rooms = max(max_rooms, num_rooms) + return max_rooms + + +# @lc code=end diff --git a/253_meeting_rooms_ii_medium/two_pointers.py b/253_meeting_rooms_ii_medium/two_pointers.py new file mode 100644 index 0000000..d0bf1dc --- /dev/null +++ b/253_meeting_rooms_ii_medium/two_pointers.py @@ -0,0 +1,29 @@ +# +# @lc app=leetcode id=253 lang=python3 +# +# [253] Meeting Rooms II +# + +# @lc code=start +class Solution: + def minMeetingRooms(self, intervals: list[list[int]]) -> int: + start_times: list[int] = [interval[0] for interval in intervals] + end_times: list[int] = [interval[1] for interval in intervals] + + start_times.sort() + end_times.sort() + max_rooms = 0 + start_time_pointer = 0 + end_time_pointer = 0 + while start_time_pointer < len(intervals): + start_time = start_times[start_time_pointer] + end_time = end_times[end_time_pointer] + if start_time < end_time: + start_time_pointer += 1 + else: + end_time_pointer += 1 + max_rooms = max(max_rooms, start_time_pointer - end_time_pointer) + return max_rooms + + +# @lc code=end From d122cc575e58e26fe278c4c34e5d5d33589c9b28 Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Wed, 10 Sep 2025 09:51:53 +0900 Subject: [PATCH 2/5] Add step3 --- 253_meeting_rooms_ii_medium/step3.py | 13 ----------- 253_meeting_rooms_ii_medium/step3_1.py | 29 +++++++++++++++++++++++++ 253_meeting_rooms_ii_medium/step3_2.py | 30 ++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 13 deletions(-) delete mode 100644 253_meeting_rooms_ii_medium/step3.py create mode 100644 253_meeting_rooms_ii_medium/step3_1.py create mode 100644 253_meeting_rooms_ii_medium/step3_2.py diff --git a/253_meeting_rooms_ii_medium/step3.py b/253_meeting_rooms_ii_medium/step3.py deleted file mode 100644 index 58b34f3..0000000 --- a/253_meeting_rooms_ii_medium/step3.py +++ /dev/null @@ -1,13 +0,0 @@ -# -# @lc app=leetcode id=253 lang=python3 -# -# [253] Meeting Rooms II -# - -# @lc code=start -class Solution: - def minMeetingRooms(self, intervals: list[list[int]]) -> int: - return 0 - - -# @lc code=end diff --git a/253_meeting_rooms_ii_medium/step3_1.py b/253_meeting_rooms_ii_medium/step3_1.py new file mode 100644 index 0000000..0046109 --- /dev/null +++ b/253_meeting_rooms_ii_medium/step3_1.py @@ -0,0 +1,29 @@ +# +# @lc app=leetcode id=253 lang=python3 +# +# [253] Meeting Rooms II +# + +# @lc code=start +class Solution: + def minMeetingRooms(self, intervals: list[list[int]]) -> int: + time_events = [] + # END must comes first when time_events is sorted. + START = 1 + END = 0 + for start_time, end_time in intervals: + time_events.append((start_time, START)) + time_events.append((end_time, END)) + time_events.sort() + num_rooms = 0 + max_num_rooms = 0 + for _, event_type in time_events: + if event_type == END: + num_rooms -= 1 + else: # START + num_rooms += 1 + max_num_rooms = max(max_num_rooms, num_rooms) + return max_num_rooms + + +# @lc code=end diff --git a/253_meeting_rooms_ii_medium/step3_2.py b/253_meeting_rooms_ii_medium/step3_2.py new file mode 100644 index 0000000..c0da26f --- /dev/null +++ b/253_meeting_rooms_ii_medium/step3_2.py @@ -0,0 +1,30 @@ +# +# @lc app=leetcode id=253 lang=python3 +# +# [253] Meeting Rooms II +# + +# @lc code=start +from heapq import heappush, heappushpop + + +class Solution: + def minMeetingRooms(self, intervals: list[list[int]]) -> int: + if not intervals: + return 0 + intervals.sort() + num_rooms = 0 + start_time, end_time = intervals[0] + heap = [] + heappush(heap, end_time) + for start_time, end_time in intervals[1:]: + earliest_end_time = heap[0] + if start_time < earliest_end_time: + heappush(heap, end_time) + continue + # use the same room after the earliest meeting ends + heappushpop(heap, end_time) + return len(heap) + + +# @lc code=end From 41db38cc64a51d33ed1865f89cf44fc5a62b9e8b Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Wed, 10 Sep 2025 21:00:04 +0900 Subject: [PATCH 3/5] Add implementations for step3 using timeline sweep and heap methods in README --- 253_meeting_rooms_ii_medium/README.md | 43 +++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/253_meeting_rooms_ii_medium/README.md b/253_meeting_rooms_ii_medium/README.md index 25b948f..391d56f 100644 --- a/253_meeting_rooms_ii_medium/README.md +++ b/253_meeting_rooms_ii_medium/README.md @@ -78,6 +78,49 @@ class Solution: ## step3 +`step3_1.py`:一度思いついてしまえばヒープよりも簡単に実装できる +```python +class Solution: + def minMeetingRooms(self, intervals: list[list[int]]) -> int: + time_events = [] + # END must comes first when time_events is sorted. + START = 1 + END = 0 + for start_time, end_time in intervals: + time_events.append((start_time, START)) + time_events.append((end_time, END)) + time_events.sort() + num_rooms = 0 + max_num_rooms = 0 + for _, event_type in time_events: + if event_type == END: + num_rooms -= 1 + else: # START + num_rooms += 1 + max_num_rooms = max(max_num_rooms, num_rooms) + return max_num_rooms +``` + +`step3_2.py`:ヒープを用いた解法 +```python +class Solution: + def minMeetingRooms(self, intervals: list[list[int]]) -> int: + if not intervals: + return 0 + intervals.sort() + start_time, end_time = intervals[0] + heap = [] + heappush(heap, end_time) + for start_time, end_time in intervals[1:]: + earliest_end_time = heap[0] + if start_time < earliest_end_time: + heappush(heap, end_time) + continue + # use the same room after the earliest meeting ends + heappushpop(heap, end_time) + return len(heap) +``` + ## step4 (FB) From f42701405c088e7e8c470a5f9e0df1222866db5a Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Wed, 10 Sep 2025 21:00:08 +0900 Subject: [PATCH 4/5] Remove unused variable num_rooms from minMeetingRooms method --- 253_meeting_rooms_ii_medium/step3_2.py | 1 - 1 file changed, 1 deletion(-) diff --git a/253_meeting_rooms_ii_medium/step3_2.py b/253_meeting_rooms_ii_medium/step3_2.py index c0da26f..cdc2171 100644 --- a/253_meeting_rooms_ii_medium/step3_2.py +++ b/253_meeting_rooms_ii_medium/step3_2.py @@ -13,7 +13,6 @@ def minMeetingRooms(self, intervals: list[list[int]]) -> int: if not intervals: return 0 intervals.sort() - num_rooms = 0 start_time, end_time = intervals[0] heap = [] heappush(heap, end_time) From 3ad362d72e1d1f43ee511a6880f8ebb7ab6dc280 Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Fri, 12 Sep 2025 21:40:50 +0900 Subject: [PATCH 5/5] Update variable name suggestion in step4 to avoid conflict with os module --- 253_meeting_rooms_ii_medium/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/253_meeting_rooms_ii_medium/README.md b/253_meeting_rooms_ii_medium/README.md index 391d56f..032f7e3 100644 --- a/253_meeting_rooms_ii_medium/README.md +++ b/253_meeting_rooms_ii_medium/README.md @@ -122,6 +122,9 @@ class Solution: ``` ## step4 (FB) +- `times`は`os`モジュールにあるので、変数名を変更した方が良い + - cf. https://docs.python.org/ja/3.13/library/os.html#os.times +