Sort a Linked List Using Merge Sort in DSA Javascript - Time & Space Complexity
When sorting a linked list with merge sort, we want to know how the time needed grows as the list gets bigger.
We ask: How does the sorting time change when the list size increases?
Analyze the time complexity of the following code snippet.
function mergeSort(head) {
if (!head || !head.next) return head;
let middle = getMiddle(head);
let nextToMiddle = middle.next;
middle.next = null;
let left = mergeSort(head);
let right = mergeSort(nextToMiddle);
return sortedMerge(left, right);
}
function sortedMerge(a, b) {
if (!a) return b;
if (!b) return a;
if (a.val <= b.val) {
a.next = sortedMerge(a.next, b);
return a;
} else {
b.next = sortedMerge(a, b.next);
return b;
}
}
function getMiddle(head) {
let slow = head, fast = head.next;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
This code sorts a linked list by splitting it into halves, sorting each half, and merging them back in order.
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.
As the list size doubles, the number of splits grows slowly (logarithmically), but merging touches all nodes each time.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 * 4 = 40 |
| 100 | About 100 * 7 = 700 |
| 1000 | About 1000 * 10 = 10,000 |
Pattern observation: Operations grow roughly as n times log n, meaning more than linear but less than quadratic growth.
Time Complexity: O(n log n)
This means the time to sort grows a bit faster than the list size but stays efficient even for large lists.
[X] Wrong: "Since merge sort splits the list, it must be slower than simple sorting methods like bubble sort."
[OK] Correct: Merge sort actually sorts faster for large lists because it avoids repeated comparisons by dividing and conquering efficiently.
Understanding merge sort on linked lists shows you can handle recursion and pointers well, a skill many interviews look for.
"What if we changed the linked list to an array? How would the time complexity of merge sort change, if at all?"
Practice
Solution
Step 1: Understand linked list access
Linked lists do not support random access, so algorithms relying on index access are inefficient.Step 2: Analyze merge sort suitability
Merge sort works by splitting and merging lists using pointers, which fits linked lists well without needing random access.Final Answer:
Merge sort efficiently handles linked lists without random access. -> Option BQuick Check:
Linked list + merge sort = efficient pointer-based sorting [OK]
- Thinking merge sort sorts in place for linked lists
- Confusing merge sort speed with bubble sort
- Assuming random access is needed for merge sort
Solution
Step 1: Identify splitting method for linked list
Since linked lists lack indexes, we use two pointers: slow and fast to find midpoint.Step 2: Explain pointer movement
Slow pointer moves one node at a time, fast pointer moves two nodes; when fast reaches end, slow is at midpoint.Final Answer:
Use two pointers: slow moves 1 step, fast moves 2 steps until fast reaches end. -> Option DQuick Check:
Two-pointer technique splits linked list correctly [OK]
- Trying to use array methods on linked lists
- Using loops with indexes on linked lists
- Splitting by removing head repeatedly
Solution
Step 1: Split the list into halves
Split 4->2->1->3 into 4->2 and 1->3.Step 2: Recursively sort and merge halves
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 -> null -> Option AQuick Check:
Sorted ascending list = 1->2->3->4 [OK]
- Confusing merge order and producing descending list
- Not fully sorting sublists before merging
- Incorrectly splitting the list
function mergeSort(head) {
if (!head || !head.next) return head;
let slow = head, fast = head;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
}
let mid = slow.next;
slow.next = null;
let left = mergeSort(head);
let right = mergeSort(mid);
return merge(left, right);
}Solution
Step 1: Analyze pointer initialization
Fast pointer starting at head causes incorrect midpoint for even length lists.Step 2: Correct mid pointer assignment
The mid pointer should be slow, not slow.next, to correctly split the list into two halves.Final Answer:
The mid pointer should be slow, not slow.next. -> Option AQuick Check:
Mid pointer assignment = slow for correct split [OK]
- Starting fast pointer at head
- Misplacing mid pointer assignment
- Incorrect base case conditions
Solution
Step 1: Understand merge sort handles duplicates
Merge sort compares node values and merges them in ascending order, preserving duplicates.Step 2: Sort and merge duplicates
Sorting 3->1->2->3->2 results in 1->2->2->3->3 as duplicates remain in sorted order.Final Answer:
1 -> 2 -> 2 -> 3 -> 3 -> null -> Option CQuick Check:
Duplicates sorted and preserved in ascending order [OK]
- Removing duplicates by mistake
- Sorting incorrectly in descending order
- Confusing linked list with array sorting
Ready to prove it?
Pass the test and this pattern moves unknown
