Sort a Linked List Using Merge Sort in DSA Python - Time & Space Complexity
We want to understand how the time needed to sort a linked list grows as the list gets bigger.
How does the sorting time change when the list size increases?
Analyze the time complexity of the following code snippet.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def merge_sort(head):
if not head or not head.next:
return head
# Find middle
slow, fast = head, head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
mid = slow.next
slow.next = None
left = merge_sort(head)
right = merge_sort(mid)
return merge(left, right)
# merge function merges two sorted lists
This code splits the linked list into halves recursively and merges sorted halves back.
- Primary operation: Recursively splitting the list and merging sorted halves.
- How many times: The list is split about log n times, and merging happens n times total.
Each split divides the list in half, and merging takes time proportional to the list size.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 40 operations |
| 100 | About 700 operations |
| 1000 | About 10,000 operations |
Pattern observation: Operations grow a bit faster than n but much slower than n squared, roughly n times log n.
Time Complexity: O(n log n)
This means the time to sort grows a little more than linearly as the list gets bigger, which is efficient for sorting.
[X] Wrong: "Since we split the list, the time should be just O(log n)."
[OK] Correct: Splitting happens log n times, but merging takes time proportional to n each time, so total time is n times log n.
Understanding merge sort on linked lists shows you can handle recursion and linked data well, a useful skill in many coding challenges.
"What if we used quicksort instead of merge sort on the linked list? How would the time complexity change?"
Practice
Solution
Step 1: Understand linked list access
Linked lists do not support fast random access, unlike arrays.Step 2: Compare sorting algorithms
Merge sort works by splitting and merging, which only needs sequential access, making it suitable for linked lists.Final Answer:
Merge sort does not require random access, which suits linked lists well. -> Option CQuick Check:
Linked list + merge sort = no random access needed [OK]
- Thinking quicksort is better for linked lists
- Believing merge sort sorts in-place without extra space
- Confusing stability properties of sorting algorithms
Solution
Step 1: Identify base case for recursion
When the list is empty or has one node, it is already sorted.Step 2: Correct base case condition
Check if head is None (empty list) or head.next is None (single node), then return head.Final Answer:
if head is None or head.next is None: return head -> Option DQuick Check:
Base case returns head when list size ≤ 1 [OK]
- Returning None instead of head for single node
- Using 'and' instead of 'or' in condition
- Missing base case leading to infinite recursion
4 -> 2 -> 1 -> 3 -> None
Solution
Step 1: Split the list into halves
Split into (4 -> 2) and (1 -> 3).Step 2: Recursively sort and merge
Sort (4 -> 2) to (2 -> 4), sort (1 -> 3) to (1 -> 3), then merge to (1 -> 2 -> 3 -> 4).Final Answer:
1 -> 2 -> 3 -> 4 -> None -> Option AQuick Check:
Sorted ascending order = 1 -> 2 -> 3 -> 4 [OK]
- Confusing merge order and producing unsorted output
- Not splitting list correctly
- Merging without sorting halves first
def merge(left, right):
if not left:
return right
if not right:
return left
if left.val < right.val:
result = left
result.next = merge(left.next, right)
else:
result = right
result.next = merge(left, right.next)
return resultSolution
Step 1: Analyze base cases
Returns right if left is None, and left if right is None, correctly handling empty lists.Step 2: Check recursive merge logic
Compares values, assigns result, and recursively merges remaining nodes correctly.Final Answer:
No bug; function correctly merges two sorted lists. -> Option BQuick Check:
Merge function logic is correct and complete [OK]
- Thinking <= is required for stability (not always needed)
- Missing base case returns
- Returning None instead of merged list
5 -> 3 -> 8 -> 2 -> 7 -> None
After applying merge sort, what will be the final linked list? Also, what is the time complexity of merge sort on this linked list?
Solution
Step 1: Sort the linked list nodes
Sorted order is 2 -> 3 -> 5 -> 7 -> 8.Step 2: Understand merge sort time complexity
Merge sort divides list and merges in O(n log n) time for n nodes.Final Answer:
2 -> 3 -> 5 -> 7 -> 8 -> None; Time complexity: O(n log n) -> Option AQuick Check:
Sorted list + merge sort time = O(n log n) [OK]
- Confusing time complexity with O(n^2)
- Not sorting nodes correctly
- Assuming merge sort is O(n)
Ready to prove it?
Pass the test and this pattern moves unknown
