-
Notifications
You must be signed in to change notification settings - Fork 0
Merge K Sorted Lists #122
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?
Merge K Sorted Lists #122
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,108 @@ | ||
| # 23. Merge k Sorted Lists | ||
|
|
||
| ## step1 | ||
|
|
||
| 17mで解けた。紙に書いて考えるうちに最小値を管理する -> heapという発想になった。破壊的な解法になっている。 | ||
|
|
||
| 時間計算量: | ||
| O(sum(lists[i].length)log k) | ||
|
|
||
| 空間計算量: | ||
| O(k) | ||
|
|
||
| ## step2 | ||
|
|
||
| 非破壊的な解法: step2.py | ||
|
|
||
| indexを格納しないと、<が定義されずエラーになる。 | ||
|
|
||
| これを面接のtest環境のないところで気がつくのは、ヒントがないと自分には難しそう。 | ||
|
|
||
| 以下の実験でも確認できる。 | ||
|
|
||
| ```python | ||
| heap = [(0, ListNode()), (0, ListNode())] | ||
| heapq.heapify(heap) | ||
| # TypeError: '<' not supported between instances of 'ListNode' and 'ListNode' | ||
| ``` | ||
|
|
||
| ### 追記 | ||
| ListNodeに__lt__メソッドを強制的に加えた実装を追加 | ||
|
|
||
| ## 他の人のコード | ||
|
|
||
| https://github.com/shining-ai/leetcode/pull/67 | ||
|
|
||
| > これ (index) がないと、定義されていないListNodeの比較になって問題が出るのですかね。 | ||
|
|
||
| - 非破壊的+heapを使わない解法: | ||
|
|
||
| ```python | ||
| lass Solution: | ||
| def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]: | ||
| lists_val = [] | ||
| for node in lists: | ||
| while node: | ||
| lists_val.append(node.val) | ||
| node = node.next | ||
| lists_val.sort() | ||
| sentinel = ListNode() | ||
| node = sentinel | ||
| for i in lists_val: | ||
| node.next = ListNode(i) | ||
| node = node.next | ||
|
|
||
| return sentinel.next | ||
| ``` | ||
|
|
||
| LinkedListの操作に気を取られて思いつかなかった。 | ||
|
|
||
| 時間計算量的はS=sum(lists[i].length)として O(Slog S)だが実際に実行してみると、heapの解法と大差なかった。 | ||
|
|
||
|
|
||
| > マージソートをイメージした解法 | ||
| > 先頭の2つのリストをマージしていき、最後の1つになるまで繰り返す | ||
|
|
||
| 自然な解法なので、面接で聞かれる可能性は高そう。これを思いつかなったのは、まだまだ修行が足りないということだろうな。 | ||
|
|
||
| 時間計算量 O(Slog k) | ||
|
|
||
| sentinelという変数名は良いな | ||
|
|
||
| ```python | ||
|
|
||
| class Solution: | ||
| def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]: | ||
| def merge_two_lists(list_1, list_2): | ||
| sentinel = ListNode() | ||
| node = sentinel | ||
| while list_1 and list_2: | ||
| if list_1.val < list_2.val: | ||
| node.next = list_1 | ||
| list_1 = list_1.next | ||
| else: | ||
| node.next = list_2 | ||
| list_2 = list_2.next | ||
| node = node.next | ||
| if not list_1: | ||
| node.next = list_2 | ||
| else: | ||
| node.next = list_1 | ||
| return sentinel.next | ||
|
|
||
| list_queue = deque(lists) | ||
| if not list_queue: | ||
| return None | ||
| while 1: | ||
| list_1 = list_queue.popleft() | ||
| if not list_queue: | ||
| return list_1 | ||
| list_2 = list_queue.popleft() | ||
| mearged_list = merge_two_lists(list_1, list_2) | ||
| list_queue.append(mearged_list) | ||
| ``` | ||
|
|
||
| ## step3 | ||
| マージソートの解法を3回書くことにする。 | ||
|
|
||
| merge_two_listsの内側でswapを用いることでsimpleになった気がする。 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import heapq | ||
|
|
||
|
|
||
| # Definition for singly-linked list. | ||
| class ListNode: | ||
| def __init__(self, val=0, next=None): | ||
| self.val = val | ||
| self.next = next | ||
|
|
||
|
|
||
| class Solution: | ||
| def mergeKLists(self, lists: list[ListNode | None]) -> ListNode | None: | ||
| heap_indices = [ | ||
| (node.val, i) for i, node in enumerate(lists) if node is not None | ||
| ] | ||
| heapq.heapify(heap_indices) | ||
| dummy = ListNode() | ||
|
|
||
| node = dummy | ||
| while heap_indices: | ||
| _, i = heapq.heappop(heap_indices) | ||
| next_node = lists[i] | ||
| node.next = next_node | ||
| lists[i] = next_node.next | ||
| if next_node.next is not None: | ||
| heapq.heappush(heap_indices, (next_node.next.val, i)) | ||
| node = node.next | ||
|
|
||
| return dummy.next |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import heapq | ||
|
|
||
|
|
||
| # Definition for singly-linked list. | ||
| class ListNode: | ||
| def __init__(self, val=0, next=None): | ||
| self.val = val | ||
| self.next = next | ||
|
|
||
|
|
||
| class Solution: | ||
| def mergeKLists(self, lists: list[ListNode | None]) -> ListNode | None: | ||
| # Include index i to avoid comparison collisions when node values are equal. | ||
| heap = [(node.val, i, node) for i, node in enumerate(lists) if node is not None] | ||
| heapq.heapify(heap) | ||
|
|
||
| dummy = ListNode() | ||
| node = dummy | ||
| while heap: | ||
| val, i, head = heapq.heappop(heap) | ||
| node.next = ListNode(val) | ||
|
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. ここを node.next = ListNode(head.val)とすればヒープにvalを持たせる必要がなくなり、heap の要素を単に node だけにできると思います
Owner
Author
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. ここも、 heap の要素が単に node だけでは順序比較ができないので、このような実装にしました。 二点のご指摘を受けて、ListNodeに__lt__メソッドを強制的に加えた実装を追加しました。 |
||
| node = node.next | ||
| if head.next is not None: | ||
| heapq.heappush(heap, (head.next.val, i, head.next)) | ||
|
|
||
| return dummy.next | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import heapq | ||
|
|
||
|
|
||
| # Definition for singly-linked list. | ||
| class ListNode: | ||
| def __init__(self, val=0, next=None): | ||
| self.val = val | ||
| self.next = next | ||
|
|
||
|
|
||
| class Solution: | ||
| def mergeKLists(self, lists: list[ListNode | None]) -> ListNode | None: | ||
| ListNode.__lt__ = lambda self, other: self.val < other.val | ||
|
|
||
| heap = [node for node in lists if node is not None] | ||
| heapq.heapify(heap) | ||
|
|
||
| dummy = ListNode() | ||
| node = dummy | ||
| while heap: | ||
| head = heapq.heappop(heap) | ||
| node.next = ListNode(head.val) | ||
| node = node.next | ||
| if head.next is not None: | ||
| heapq.heappush(heap, head.next) | ||
|
|
||
| return dummy.next |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| # Definition for singly-linked list. | ||
| import collections | ||
|
|
||
|
|
||
| class ListNode: | ||
| def __init__(self, val=0, next=None): | ||
| self.val = val | ||
| self.next = next | ||
|
|
||
|
|
||
| class Solution: | ||
| def mergeKLists(self, lists: list[ListNode | None]) -> ListNode | None: | ||
| if not lists: | ||
| return None | ||
|
|
||
| def merge_two_lists(node1, node2): | ||
| sentinel = ListNode() | ||
| node = sentinel | ||
|
|
||
| while node1 is not None and node2 is not None: | ||
| if node1.val > node2.val: | ||
| node1, node2 = node2, node1 | ||
| node.next = node1 | ||
| node = node.next | ||
| node1 = node1.next | ||
|
|
||
| node.next = node1 if node1 is not None else node2 | ||
| return sentinel.next | ||
|
|
||
| node_to_merge = collections.deque(lists) | ||
| while 1: | ||
| node1 = node_to_merge.popleft() | ||
|
|
||
| if not node_to_merge: | ||
| return node1 | ||
|
|
||
| node2 = node_to_merge.popleft() | ||
| node_to_merge.append(merge_two_lists(node1, node2)) |
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.
i は使われていないので不要そうです
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.
indexを格納しないと、valが等しい場合、< が定義されずエラーが起こります。
コメントなどで補おうと思います。