-
Notifications
You must be signed in to change notification settings - Fork 0
253 meeting rooms ii medium #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
de8341b
d122cc5
41db38c
f427014
3ad362d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,193 @@ | ||
| # 問題へのリンク | ||
| [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 | ||
|
|
||
| `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() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 入力を変更している点が気になりました。 |
||
| start_time, end_time = intervals[0] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. start_time は使っていないので |
||
| heap = [] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 上で書かれているように meeting_end_times の方がわかりやすいと思いました。また、直後で heappush しているので heap であることがわかり、step1 よりも読みやすかったです。 |
||
| 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) | ||
| - `times`は`os`モジュールにあるので、変数名を変更した方が良い | ||
| - cf. https://docs.python.org/ja/3.13/library/os.html#os.times | ||
|
|
||
|
|
||
|
|
||
|
|
||
| # 別解・模範解答 | ||
|
|
||
| ## 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/) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # | ||
| # @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() | ||
| 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
START, END と対応して num_rooms を +1, -1 しているので、STARTを開始の印とするだけではなくて部屋の変化数 +1、ENDを部屋の変化数 -1 とする方が値に必然性がでて良いかなと個人的には思います。
https://github.com/tokuhirat/LeetCode/pull/56/files#r2292380034