Merge k Sorted Lists hard
Problem Statement
Merge k sorted linked lists into one sorted linked list.
Example 1
Input: lists = [[1,4,5],[1,3,4],[2,6]] Output: [1,1,2,3,4,4,5,6] Explanation: The linked-lists are: [ 1->4->5, 1->3->4, 2->6 ] merging them into one sorted list: 1->1->2->3->4->4->5->6
Example 2
Input: lists = [] Output: []
Steps
-
Handle Empty Input: If the input list
lists
is empty, return an empty list. -
Choose a Merge Strategy: We'll use a divide-and-conquer approach combined with a merge-two-sorted-lists helper function. This is more efficient than other approaches like repeatedly merging two lists at a time.
-
Divide and Conquer: Recursively merge pairs of lists until only one list remains.
-
Merge Two Sorted Lists: A helper function will efficiently merge two sorted linked lists.
-
Return the Result: The final merged list is returned.
Explanation
The core idea is to recursively merge sub-problems. Imagine merging four sorted lists (A, B, C, D). We can merge A and B, C and D separately, then merge the results of these merges. This reduces the problem to merging smaller sets of lists. The base case is when we have just one list, which is already sorted.
The mergeTwoLists
function is a standard merge operation similar to merging two sorted arrays. It iteratively compares the heads of the two lists and adds the smaller node to the merged list.
Code
# 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]) -> ListNode:
if not lists:
return None
def mergeTwoLists(l1, l2):
dummy = ListNode() # Dummy node to simplify the head insertion
tail = dummy
while l1 and l2:
if l1.val < l2.val:
tail.next = l1
l1 = l1.next
else:
tail.next = l2
l2 = l2.next
tail = tail.next
tail.next = l1 or l2 # Attach the remaining list
return dummy.next
while len(lists) > 1:
mergedLists = []
for i in range(0, len(lists), 2):
l1 = lists[i]
l2 = lists[i + 1] if i + 1 < len(lists) else None
mergedLists.append(mergeTwoLists(l1, l2))
lists = mergedLists
return lists[0] if lists else None
# Example Usage
list1 = ListNode(1, ListNode(4, ListNode(5)))
list2 = ListNode(1, ListNode(3, ListNode(4)))
list3 = ListNode(2, ListNode(6))
solution = Solution()
merged_list = solution.mergeKLists([list1, list2, list3])
result = []
while merged_list:
result.append(merged_list.val)
merged_list = merged_list.next
print(result) # Output: [1, 1, 2, 3, 4, 4, 5, 6]
Complexity
-
Time Complexity: O(N log k), where k is the number of linked lists and N is the total number of nodes. The merge operation in
mergeTwoLists
is O(N) for each merge level and we have at most log k levels of merge operations in our divide and conquer approach. -
Space Complexity: O(1) in-place merging (excluding the space for the result list itself, which is O(N)) if we consider recursive call stack space as constant. If we consider the space used by recursive calls, then it is O(log k) because of the recursion depth in divide and conquer. We only use a constant amount of extra space during the merge operations.