[LeetCode解題攻略] 23. Merge k Sorted Lists

更新於 發佈於 閱讀時間約 13 分鐘

題目描述

給定 k 個遞增排序的鏈表,將它們合併成一個遞增排序的鏈表,並返回合併後的鏈表。

範例 1:

Input: lists = [[1,4,5],[1,3,4],[2,6]]
Output: [1,1,2,3,4,4,5,6]

範例 2:

Input: lists = []
Output: []

範例 3:

Input: lists = [[]]
Output: []

解題思路

k 個排序鏈表合併成一個排序鏈表是經典的合併問題,可以考慮以下幾種解法:

  1. 暴力解法:將所有節點收集到一個列表中,排序後再組裝成一個新的鏈表。
  2. 兩兩合併:反覆將兩個鏈表合併,直到所有鏈表合併完成。
  3. 分治法:類似歸併排序,將 k 個鏈表分成兩半,遞歸合併。
  4. 優先隊列/最小堆:利用最小堆(優先隊列)高效地合併節點。

解法一:暴力解法

思路

  1. 遍歷所有鏈表節點,將它們的值存入一個列表中。
  2. 對列表進行排序。
  3. 根據排序後的結果構建新的鏈表。

程式碼

# 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[Optional[ListNode]]) -> Optional[ListNode]:
nodes = []

# 收集所有節點的值
for l in lists:
while l:
nodes.append(l.val)
l = l.next

# 排序節點
nodes.sort()

# 根據排序結果重新構建鏈表
dummy = ListNode()
current = dummy
for val in nodes:
current.next = ListNode(val)
current = current.next

return dummy.next

時間與空間複雜度

  • 時間複雜度:收集節點的值需要 O(N)(其中 N 是所有鏈表節點的總數),排序需要 O(N log N),總時間複雜度為 O(N log N)。
  • 空間複雜度:O(N),使用了額外的列表來存儲節點值。

解法二:兩兩合併

思路

  1. 使用合併兩個排序鏈表的方式,依次合併鏈表。
  2. 反覆合併,直到所有鏈表合併成一個。

程式碼

# 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[Optional[ListNode]]) -> Optional[ListNode]:
if not lists:
return None

while len(lists) > 1:
merged_lists = []

# Merge two lists at a time
for i in range(0, len(lists), 2):
l1 = lists[i]
l2 = lists[i + 1] if i + 1 < len(lists) else None
merged_lists.append(self.mergeTwoLists(l1, l2))

lists = merged_lists

return lists[0]

def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
dummy = ListNode()
current = dummy

while l1 and l2:
if l1.val < l2.val:
current.next = l1
l1 = l1.next
else:
current.next = l2
l2 = l2.next
current = current.next

current.next = l1 if l1 else l2
return dummy.next

時間與空間複雜度

  • 時間複雜度
    • 每次合併需要 O(N),總共需要合併 log k 次,總時間複雜度為 O(N log k),其中 N 是總節點數,k 是鏈表數量。
  • 空間複雜度
    • 使用了遞迴合併兩個鏈表,但遞迴深度固定,額外空間為 O(1)。

解法三:分治法

思路

  1. 使用分治法,將鏈表分為左右兩半。
  2. 遞歸地合併兩部分。
  3. 最後返回合併後的結果。

程式碼

# 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[Optional[ListNode]]) -> Optional[ListNode]:
if not lists:
return None
if len(lists) == 1:
return lists[0]

mid = len(lists) // 2
left = self.mergeKLists(lists[:mid])
right = self.mergeKLists(lists[mid:])
return self.mergeTwoLists(left, right)

def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
dummy = ListNode()
current = dummy

while l1 and l2:
if l1.val < l2.val:
current.next = l1
l1 = l1.next
else:
current.next = l2
l2 = l2.next
current = current.next

current.next = l1 if l1 else l2
return dummy.next

時間與空間複雜度

  • 時間複雜度
    • 與兩兩合併法相同,為 O(N log k)。
  • 空間複雜度
    • 使用了遞迴分治,遞迴深度為 O(log k),空間複雜度為 O(log k)。

解法四:最小堆法(優先隊列)

思路

  1. 使用優先隊列(最小堆)存儲每個鏈表的當前節點。
  2. 每次取出最小的節點,並將其後繼節點加入堆中。
  3. 重複上述步驟,直到所有節點被處理完。

程式碼

# 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[Optional[ListNode]]) -> Optional[ListNode]:
class HeapNode:
def __init__(self, node):
self.node = node

def __lt__(self, other):
return self.node.val < other.node.val

heap = []

# 初始化堆
for l in lists:
if l:
heapq.heappush(heap, HeapNode(l))

dummy = ListNode()
current = dummy

while heap:
# 取出最小節點
smallest = heapq.heappop(heap).node
current.next = smallest
current = current.next

# 如果還有後繼節點,加入堆中
if smallest.next:
heapq.heappush(heap, HeapNode(smallest.next))

return dummy.next

時間與空間複雜度

  • 時間複雜度
    • 每次操作堆需要 O(log k),處理所有節點需要 O(N log k),總時間複雜度為 O(N log k)。
  • 空間複雜度
    • 優先隊列存儲了最多 k 個節點,空間複雜度為 O(k)。

總結

對於 k 值較小時,分治法和兩兩合併法都表現不錯;當 k 值較大時,優先隊列法因其高效性表現最佳。

留言
avatar-img
留言分享你的想法!
avatar-img
追極光的北極熊|軟體工程師的小天地
9會員
152內容數
歡迎來到我的部落格!這裡記錄了軟體工程師的日常生活點滴,並分享程式設計與演算法的實用教學。無論你是初學者還是有經驗的開發者,都能在這裡找到深入淺出的技術解析與實戰技巧。此外,我也會分享工作中的心路歷程與學習心得,讓你不僅學到技術,更能瞭解軟體開發的實際應用與挑戰。希望透過這個平台,能與你共同成長,激發對技術的熱情!
2025/04/04
給定一個非負整數陣列 nums,其中 nums[i] 代表你在索引 i 處最多可以向右跳幾步。 請判斷是否能夠從索引 0 跳到最後一個索引。
2025/04/04
給定一個非負整數陣列 nums,其中 nums[i] 代表你在索引 i 處最多可以向右跳幾步。 請判斷是否能夠從索引 0 跳到最後一個索引。
2025/03/28
給定一個 m x n 的矩陣 matrix,請按照 螺旋順序(spiral order) 返回矩陣中的所有元素。
2025/03/28
給定一個 m x n 的矩陣 matrix,請按照 螺旋順序(spiral order) 返回矩陣中的所有元素。
2025/03/20
這道題是 LeetCode 的經典題之一,要求我們找出 一個子陣列,使其和最大,並返回這個最大和的數值。
2025/03/20
這道題是 LeetCode 的經典題之一,要求我們找出 一個子陣列,使其和最大,並返回這個最大和的數值。
看更多
你可能也想看
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
全球科技產業的焦點,AKA 全村的希望 NVIDIA,於五月底正式發布了他們在今年 2025 第一季的財報 (輝達內部財務年度為 2026 Q1,實際日曆期間為今年二到四月),交出了打敗了市場預期的成績單。然而,在銷售持續高速成長的同時,川普政府加大對於中國的晶片管制......
Thumbnail
全球科技產業的焦點,AKA 全村的希望 NVIDIA,於五月底正式發布了他們在今年 2025 第一季的財報 (輝達內部財務年度為 2026 Q1,實際日曆期間為今年二到四月),交出了打敗了市場預期的成績單。然而,在銷售持續高速成長的同時,川普政府加大對於中國的晶片管制......
Thumbnail
這個問題「Remove Duplicates from Sorted Array」要求我們從一個已排序的陣列中移除重複的元素,並且返回移除後的陣列新長度。由於陣列已經是排序好的,所以所有的重複元素會是相鄰的。 我們需要移除重複的元素,使每個元素最多只出現一次,並返回去重後的陣列長度。不能使用額外的空
Thumbnail
這個問題「Remove Duplicates from Sorted Array」要求我們從一個已排序的陣列中移除重複的元素,並且返回移除後的陣列新長度。由於陣列已經是排序好的,所以所有的重複元素會是相鄰的。 我們需要移除重複的元素,使每個元素最多只出現一次,並返回去重後的陣列長度。不能使用額外的空
Thumbnail
Insert Greatest Common Divisors in Linked List 題目給定一個鏈結串列, 請在兩兩節點之間加入一個新節點,新節點的值為兩者之間的最大公因數。 最後返回新串列的head node作為答案。
Thumbnail
Insert Greatest Common Divisors in Linked List 題目給定一個鏈結串列, 請在兩兩節點之間加入一個新節點,新節點的值為兩者之間的最大公因數。 最後返回新串列的head node作為答案。
Thumbnail
題目敘述 題目會給定一個鏈結串列 Linked List的頭部結點,要求我們根據索引的奇偶數重新排列。奇數索引的在前,偶數索引的在後。數的時候,從Head節點的索引=1開始數。 例如: 1 -> 2 -> 3 -> 4 -> 5 重新排列為 1 -> 3 -> 5 -> 2 -> 4
Thumbnail
題目敘述 題目會給定一個鏈結串列 Linked List的頭部結點,要求我們根據索引的奇偶數重新排列。奇數索引的在前,偶數索引的在後。數的時候,從Head節點的索引=1開始數。 例如: 1 -> 2 -> 3 -> 4 -> 5 重新排列為 1 -> 3 -> 5 -> 2 -> 4
Thumbnail
題目敘述 題目會給我們一個鏈結串列的頭部結點Head node,要求我們計算鏈結串列中的Twin sum最大值是多少? 註: Twin Sum的定義就是頭尾結點相對位置相同的,互相配對加總在一起的值。 例如 給定串列= 1 -> 3 -> 2 -> 5 -> 100 -> 8 1, 8 一組
Thumbnail
題目敘述 題目會給我們一個鏈結串列的頭部結點Head node,要求我們計算鏈結串列中的Twin sum最大值是多少? 註: Twin Sum的定義就是頭尾結點相對位置相同的,互相配對加總在一起的值。 例如 給定串列= 1 -> 3 -> 2 -> 5 -> 100 -> 8 1, 8 一組
Thumbnail
題目 : 83. Remove Duplicates from Sorted List
Thumbnail
題目 : 83. Remove Duplicates from Sorted List
Thumbnail
題目會給我們兩條已經從小到大排序好的串列,要求我們依照從小到大的順序,合併這兩條串列。
Thumbnail
題目會給我們兩條已經從小到大排序好的串列,要求我們依照從小到大的順序,合併這兩條串列。
Thumbnail
題目:在此 kata 中,您將創建一個包含列表並返回具有相反順序的列表的函數。範例(輸入->輸出) * [1, 2, 3, 4] -> [4, 3, 2, 1] * [9, 2, 0, 7] -> [7, 0, 2, 9]
Thumbnail
題目:在此 kata 中,您將創建一個包含列表並返回具有相反順序的列表的函數。範例(輸入->輸出) * [1, 2, 3, 4] -> [4, 3, 2, 1] * [9, 2, 0, 7] -> [7, 0, 2, 9]
Thumbnail
題目的輸入會給我們一個串列,要求我們從頭到尾反轉整個串列。 例子: 如果輸入是1 -> 2 -> 3,那麼輸出就是 3 -> 2 -> 1
Thumbnail
題目的輸入會給我們一個串列,要求我們從頭到尾反轉整個串列。 例子: 如果輸入是1 -> 2 -> 3,那麼輸出就是 3 -> 2 -> 1
Thumbnail
這題就是經典的考排序驗算法, 不管在教科書、上機考、面試白板題都是一個很基本又滿熱門的題目。 題目會給定一個輸入陣列,要求我們實作一個排序演算法,把陣列元素從小到大排好。
Thumbnail
這題就是經典的考排序驗算法, 不管在教科書、上機考、面試白板題都是一個很基本又滿熱門的題目。 題目會給定一個輸入陣列,要求我們實作一個排序演算法,把陣列元素從小到大排好。
Thumbnail
Merge Two Sorted Lists : 將兩個LinkedList合併成一個新的陣列,並將內容由小到大排序。
Thumbnail
Merge Two Sorted Lists : 將兩個LinkedList合併成一個新的陣列,並將內容由小到大排序。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News