Sort a Linked List Using Merge Sort in DSA C++ - Time & Space Complexity
We want to understand how the time to sort a linked list grows as the list gets bigger.
Specifically, we ask: How many steps does merge sort take to sort a linked list of size n?
Analyze the time complexity of the following code snippet.
// Merge sort for linked list
ListNode* mergeSort(ListNode* head) {
if (!head || !head->next) return head;
ListNode* mid = getMiddle(head);
ListNode* left = head;
ListNode* right = mid->next;
mid->next = nullptr;
left = mergeSort(left);
right = mergeSort(right);
return merge(left, right);
}
This code splits the list into halves, sorts each half recursively, then merges the sorted halves.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursively splitting the list and merging sorted halves.
- How many times: The list is split about log n times, and merging touches all nodes each time.
Each split divides the list in half, and merging combines all nodes once per level.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 * 4 = 40 steps |
| 100 | About 100 * 7 = 700 steps |
| 1000 | About 1000 * 10 = 10,000 steps |
Pattern observation: Operations grow roughly as n times log n, meaning doubling input size more than doubles the work.
Time Complexity: O(n log n)
This means the time to sort grows a bit faster than linearly but much slower than quadratic as the list grows.
[X] Wrong: "Merge sort on linked list is O(n²) because of recursion and merging."
[OK] Correct: Each merge step processes all nodes once per level, and the number of levels is log n, so total work is n log n, not n squared.
Understanding merge sort on linked lists shows you how to handle recursion and splitting data efficiently, a key 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 merge sort time complexity on linked lists
Merge sort divides the list and merges sorted halves, achieving O(n log n) time.Step 2: Compare space usage with other sorts
Unlike quicksort or heapsort on arrays, merge sort on linked lists does not need extra arrays, so it uses less extra space.Final Answer:
It efficiently sorts the list in O(n log n) time without extra space for arrays. -> Option CQuick Check:
Merge sort = O(n log n) + low extra space [OK]
- Thinking merge sort uses extra arrays for linked lists
- Confusing merge sort with bubble sort time complexity
- Assuming linked lists must be converted to arrays first
Solution
Step 1: Identify the standard splitting technique
Using two pointers, slow and fast, where fast moves twice as fast, slow ends at middle.Step 2: Explain why other options are incorrect
Counting nodes is less efficient; dividing values is meaningless; recursion needs splitting first.Final Answer:
Use two pointers: slow moves 1 step, fast moves 2 steps; slow marks middle. -> Option BQuick Check:
Two-pointer split = Use two pointers: slow moves 1 step, fast moves 2 steps; slow marks middle. [OK]
- Trying to split by counting nodes first
- Splitting based on node values instead of positions
- Not splitting before recursive calls
Solution
Step 1: Break list into halves and sort recursively
Split into (4 -> 2) and (1 -> 3), sort each: (2 -> 4), (1 -> 3).Step 2: Merge sorted halves
Merge (2 -> 4) and (1 -> 3) to get (1 -> 2 -> 3 -> 4).Final Answer:
1 -> 2 -> 3 -> 4 -> null -> Option AQuick Check:
Sorted ascending = 1 -> 2 -> 3 -> 4 -> null [OK]
- Confusing merge order and producing descending list
- Not fully sorting both halves before merging
- Merging without comparing node values
Node* mergeSort(Node* head) {
if (!head || !head->next) return head;
Node* slow = head;
Node* fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
Node* mid = slow->next;
slow->next = nullptr;
Node* left = mergeSort(head);
Node* right = mergeSort(mid);
return merge(left, right);
}Solution
Step 1: Analyze pointer initialization
Fast pointer starting at head causes incorrect middle for even length lists.Step 2: Correct initialization for proper middle detection
Fast should start at head->next to ensure slow points to middle correctly.Final Answer:
Fast pointer should start at head->next, not head. -> Option DQuick Check:
Fast pointer start = head->next [OK]
- Starting fast pointer at head causing wrong split
- Confusing slow and fast pointer speeds
- Ignoring base case for single node
Solution
Step 1: Understand merge sort handles duplicates by comparing values
During merge, nodes with equal values are placed in order preserving duplicates.Step 2: Sort the list step-by-step
Sorted order is ascending: 3, 3, 5, 5, 8.Final Answer:
3 -> 3 -> 5 -> 5 -> 8 -> null -> Option AQuick Check:
Duplicates preserved and sorted ascending [OK]
- Assuming duplicates are removed
- Sorting in descending order by mistake
- Not merging duplicates correctly
Ready to prove it?
Pass the test and this pattern moves unknown
