LeetCode 23 - Merge k Sorted Lists
題目
題目連結:https://leetcode.com/problems/merge-k-sorted-lists/
合併 k 個已排序好的鏈結串列成一個排序好的鏈結串列。
範例說明
1 | Input: |
想法
以下簡稱 Linked list 為串列。
方法一:時間複雜度 O(KN)、空間複雜度 O(KN)
合併兩個長度分別為 N, M,排序好的串列,我們可以用雙指針遍歷兩個串列,每次都將比較小的值加入一個新的序列中,時間複雜度為 O(N + M),空間複雜度為 O(N + M)。見下圖:
假設所有串列之總長為 N。
如果每次逐次將第一、二個串列合併,再將合併結果與第三個串列合併、再將合併結果與第四個串列合併,最終我們合併了 k - 1 次,每次合併的時間複雜度不超過 O(N)。
所以總時間複雜度為 O(KN),空間複雜度為 O(KN)。
方法二:時間複雜度 O(NlogK)、空間複雜度 O(1)
先考慮合併序列的方法,若改為先將串列兩兩配對合併,下一輪再將合併過後的串列兩兩配對合併…,直到剩下一個串列為止。如下圖:
每一輪合併,串列的數量減半,總共合併了 logK + 1 輪。再加上每一輪都會遍歷所有的串列每一個數字,總長度為 N。總時間複雜度降為 O(NLogK)。
再來改善記憶體空間的使用,若合併兩序列能不花費額外的空間儲存,即可做到空間複雜度 O(1)。
解決辦法其實也很簡單,就是做 in-place(原地)合併。
合併兩串列時 lhs, rhs 時,若 lhs->val < rhs->val,則 lhs 即為合併後串列的頭,且 lhs->next 會等於合併 lhs->next, rhs 兩串列的結果。反之亦然,若 lhs->val > rhs->val,則 rhs 即為合併後串列的頭,且 rhs->next 為合併 lhs, rhs->next 兩串列的結果。
舉例來說,串列 lhs=1->3->7->8, rhs=2->4->5->6。因為 lhs->val = 1 < 2 = rhs->val,所以 lhs 為合併 lhs, rhs 後串列的頭,而 lhs->next 等於合併 lhs->next=3->7->8, rhs=2->4->5->6 兩串列的結果。
實作細節
合併 K 個串列
筆者是這樣思考的:x <- y 為將串列 y 合併進串列 x。
- 第一輪:
0 <- 1,2 <- 3,4 <- 5,6 <- 7…x = 0, 2, 4, 6 ...,y = x + 1 - 第二輪:
0 <- 2,4 <- 6,8 <- 10,12 <- 14…x = 0, 4, 8, 12 ...,y = x + 2 - 第三輪:
0 <- 4,8 <- 12,16 <- 20,24 <- 28…x = 0, 8, 16, 24 ...,y = x + 4
總結來說,第 k 輪:
所以:
1 | // i = 2^(k-1), so i = 1, 2, 4, 8, .... |
合併兩個串列
寫成遞迴的形式,見下方程式碼 inplaceMerge。
程式碼
1 | /** |