Problem Statement in English
You’re given a list of linked lists. The elements of each linked list are sorted in ascending order.
Your task is to merge all the linked lists into one sorted (in ascending order) linked list and return the head to that list.
Approach
The naive approach would be to just iterate over linked list in the main list, which would have a time complexity of $O(n^2)$. It’s basically iterating over a matrix at that point. This is the bruteforce approach.
Try this and you’ll get a TLE (Time Limit Exceeded). How do I know? I tried it. Hehe.
We need something smarter. Enter Merge Sort. That’s it.
graph TD
A((1,2,3,4)) --> B((1,2))
A --> C((3,4))
B --> D((1))
B --> E((2))
C --> F((3))
C --> G((4))
Now, why is this better? It’s classic divide and conquer.
The recursion relation for this is given by: $2T(\frac{n}{2}) + O(n)$ , which is of the form $aT(\frac{n}{b})+ f(x)$, where:
- a is the number of problems each stage is divided into
- b is the size of each sub problem
- f(x) is the cost of combining the solutions to 2 such problems
On solving this (it fits the Master Theorem) you get a time complexity of $O(nlog(n))$. Which is a nice improvement from $O(n^2)$.
From a code perspective, we can reuse the code for merging 2 sorted linked lists, which is a classic problem in itself. We can use that to merge 2 lists at a time, and keep doing that until we have only 1 list left.
And we’re done!
Solution in Python
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
if not lists or len(lists) == 0:
return None
while len(lists) > 1:
temp = []
for i in range(0, len(lists), 2):
l1 = lists[i]
l2 = lists[i+1] if i + 1 < len(lists) else None
temp.append(self.merge_lists(l1, l2))
lists = temp
return lists[0]
def merge_lists(self, l1, l2):
node = ListNode()
ans = node
while l1 and l2:
if l1.val > l2.val:
node.next = l2
l2 = l2.next
else:
node.next = l1
l1 = l1.next
node = node.next
if l1:
node.next = l1
else:
node.next = l2
return ans.next
Complexity
Time: $O(nlog(n))$
As was explained above, the time complexity is $O(nlog(n))$ due to the divide and conquer approach.Space: $O(1)$
Since we are not using any extra space, the space complexity is $O(1)$.
Mistakes I Made
As I said, I tried to brute force it.
And we are done.