-
Notifications
You must be signed in to change notification settings - Fork 0
Solved Arai60/253. Meeting Rooms II #55
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
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,144 @@ | ||
| ## Step 1. Initial Solution | ||
|
|
||
| - 前問と同じような構造 | ||
| - 誰かがやっていた累積和の方法だとやりやすい? | ||
| - メモリを時間分確保するやり方だったか?それだと効率はあまりよくなさそう | ||
| - 他に良いやり方を思い出せなかったのでheapを使った処理を元に考える | ||
| - 終了時刻を保持しながら次のスタート時間を見ていけば重複数の最大が分かる | ||
|
|
||
| ```python | ||
| """ | ||
| Definition of Interval: | ||
| class Interval(object): | ||
| def __init__(self, start, end): | ||
| self.start = start | ||
| self.end = end | ||
| """ | ||
|
|
||
| def interval_lt(self, other) -> bool: | ||
| if self.start < other.start: | ||
| return True | ||
| return False | ||
|
|
||
| Interval.__lt__ = interval_lt | ||
|
|
||
| class Solution: | ||
| def minMeetingRooms(self, intervals: List[Interval]) -> int: | ||
| heapq.heapify(intervals) | ||
| required_days = 0 | ||
| sorted_end_times = [] | ||
| while intervals: | ||
| interval = heapq.heappop(intervals) | ||
| while sorted_end_times and interval.start >= sorted_end_times[-1]: | ||
| sorted_end_times.pop() | ||
| sorted_end_times.append(interval.end) | ||
| sorted_end_times.sort(reverse=True) | ||
| required_days = max(required_days, len(sorted_end_times)) | ||
| return required_days | ||
| ``` | ||
|
|
||
| ### Complexity Analysis | ||
|
|
||
| - 時間計算量:O(n^2 log n) | ||
| - n回のpopの中に最大nlognのソートが入る | ||
| - n < 500なので最大250000*9=2.25*10^6 → 10^8 Steps/s なら 50ms程度 | ||
| - 空間計算量:O(n) | ||
|
|
||
| ## Step 2. Alternatives | ||
|
|
||
| - AppendからのSortは効率が悪い | ||
| - bisect_leftからのinsertの方が良い? | ||
| - 以下だとend_indexの位置が間違って取得される | ||
| - 数十分かけてもよく分からなかったので一旦置いておく | ||
|
|
||
| ```python | ||
| end_index = bisect.bisect(sorted_end_times, interval.end, key=lambda x: -x) | ||
| sorted_end_times.insert(end_index, interval.end) | ||
| ``` | ||
|
|
||
| - insortでも良い | ||
| - こちらはちゃんと動いた | ||
|
|
||
| ```python | ||
| bisect.insort(sorted_end_times, interval.end, key=lambda x: -x) | ||
| ``` | ||
|
|
||
| - ドキュメントをよく読むと以下のような記述があった | ||
| - insort | ||
| - `To support inserting records in a table, the *key* function (if any) is applied to *x* for the search step but not for the insertion step.` | ||
| - bisect_left | ||
| - `To support searching complex records, the key function is not applied to the *x* value.` | ||
| - なるほど、検索時にkeyがxに適用されるかされないかの違いがあったのか | ||
| - insortを使わない場合は昇順でpop(0)を許容するか、-1倍した値を保持しておくか | ||
|
|
||
| ```python | ||
| while sorted_end_times and interval.start >= sorted_end_times[0]: | ||
| sorted_end_times.pop(0) | ||
| end_index = bisect.bisect(sorted_end_times, interval.end) | ||
| sorted_end_times.insert(end_index, interval.end) | ||
| ``` | ||
|
|
||
| ```python | ||
| while sorted_end_times and interval.start >= -sorted_end_times[-1]: | ||
| sorted_end_times.pop() | ||
| end_index = bisect.bisect(sorted_end_times, -interval.end) | ||
| sorted_end_times.insert(end_index, -interval.end) | ||
| ``` | ||
|
|
||
| - https://github.com/tokuhirat/LeetCode/pull/56/files#diff-e176b724e8fbbafc36b447606ee37b1d5b91eca27193941d2c6e233c3c1c28c7R4 | ||
| - 累積和はやはりメモリを時間分確保するやり方か | ||
| - シンプルだが効率は悪い | ||
| - その後で座標圧縮をしているが少し理解が難しかった | ||
| - List[List[int]]の展開にsum(intervals, [])を使っているのは初めて見た | ||
| - https://github.com/olsen-blue/Arai60/pull/57/files#r2030075157 | ||
| - このやり方は確かにありか | ||
| - 自分はそもそも累積和を考える時にdict型を使えば良いのでは?と思った | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def minMeetingRooms(self, intervals: List[Interval]) -> int: | ||
| meeting_num_change = defaultdict(int) | ||
| for interval in intervals: | ||
| meeting_num_change[interval.start] += 1 | ||
| meeting_num_change[interval.end] -= 1 | ||
|
|
||
| required_days = 0 | ||
| num_meetings = 0 | ||
| for change_time in sorted(meeting_num_change.keys()): | ||
| num_meetings += meeting_num_change[change_time] | ||
| required_days = max(required_days, num_meetings) | ||
| return required_days | ||
| ``` | ||
|
|
||
| - [https://github.com/shining-ai/leetcode/blob/main/arai60/54-60_others/56_253_Meeting Rooms II/level_5.py](https://github.com/shining-ai/leetcode/blob/main/arai60/54-60_others/56_253_Meeting%20Rooms%20II/level_5.py) | ||
| - 終了時間だけを管理しておく方法 | ||
| - 新しく始める時に前のミーティングが終わっていたら使うみたいなイメージのよう | ||
| - このイメージだとミーティング数を数えるのは最後のstartの後だけで済むのか | ||
|
|
||
| ## Step 3. Final Solution | ||
|
|
||
| - 元の実装の改良版で落ち着いた | ||
|
|
||
| ```python | ||
| import bisect | ||
|
|
||
| def interval_lt(self, other): | ||
| if self.start < other.start: | ||
| return True | ||
| return False | ||
|
|
||
| Interval.__lt__ = interval_lt | ||
|
|
||
| class Solution: | ||
| def minMeetingRooms(self, intervals: List[Interval]) -> int: | ||
| heapq.heapify(intervals) | ||
| end_times = [] | ||
| required_days = 0 | ||
| while intervals: | ||
| interval = heapq.heappop(intervals) | ||
| while end_times and end_times[-1] <= interval.start: | ||
| end_times.pop() | ||
| bisect.insort(end_times, interval.end, key=lambda x: -x) | ||
|
Comment on lines
+139
to
+141
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. 実はend_timesの方も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. import heapq
class Solution:
def minMeetingRooms(self, intervals: List[Interval]) -> int:
remaining_intervals = [
(interval.start, interval.end) for interval in intervals
]
heapq.heapify(remaining_intervals)
holding_end_times = []
required_days = 0
while remaining_intervals:
next_start, next_end = heapq.heappop(remaining_intervals)
while holding_end_times and next_start >= holding_end_times[0]:
heapq.heappop(holding_end_times)
heapq.heappush(holding_end_times, next_end)
required_days = max(required_days, len(holding_end_times))
return required_daysThere 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. 一重ループで書くとこうなります。 import heapq
class Solution:
def minMeetingRooms(self, intervals: list[Interval]) -> int:
start_times = [interval.start for interval in intervals]
end_times = [interval.end for interval in intervals]
heapq.heapify(start_times)
heapq.heapify(end_times)
required_days = 0
max_required_days = 0
while start_times and end_times:
if start_times[0] < end_times[0]:
heapq.heappop(start_times)
required_days += 1
max_required_days = max(max_required_days, required_days)
else:
heapq.heappop(end_times)
required_days -= 1
return max_required_daysThere 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. 上のコードをGemini 3 Proにレビューしてもらったら、
とのことでした。これはそうかもしれません。 時間計算量はheapifyはO(n)ですが、ループ内でのheappopがn回行われるため全体O(n log n)で、実質ソートと同じコストがかかり、定数倍の勝負です。これは綿密にチューニングされたsort()に平均的に勝てる気がしないですね。 |
||
| required_days = max(required_days, len(end_times)) | ||
| return required_days | ||
| ``` | ||
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.
確かにこのように書けますが、可読性・保守性いずれの観点からも個人的には避けたいと感じました。
以下のようなナイーブな書き方で十分かなと感じます。