Sort a Linked List Using Merge Sort in DSA C# - Time & Space Complexity
We want to understand how long it takes to sort a linked list using merge sort.
Specifically, how the time grows as the list gets bigger.
Analyze the time complexity of the following code snippet.
public ListNode MergeSort(ListNode head) {
if (head == null || head.next == null) return head;
ListNode middle = GetMiddle(head);
ListNode nextToMiddle = middle.next;
middle.next = null;
ListNode left = MergeSort(head);
ListNode right = MergeSort(nextToMiddle);
return SortedMerge(left, right);
}
This code splits the list into halves, sorts each half recursively, then merges them.
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, but merging touches all nodes each time.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 * log 10 ≈ 33 |
| 100 | About 100 * log 100 ≈ 664 |
| 1000 | About 1000 * log 1000 ≈ 9966 |
Pattern observation: Operations grow a bit faster than the list size, roughly n times log n.
Time Complexity: O(n log n)
This means the time grows a little faster than the list size, but still efficient for large lists.
[X] Wrong: "Merge sort on linked list is O(n²) because of recursion and merging."
[OK] Correct: Each merge step processes all nodes once, and the number of splits is about log n, so total work is n log n, not n squared.
Understanding merge sort on linked lists shows you can handle recursion and pointers well, 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 into halves recursively and merges them, 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 require extra array space, making it memory efficient.Final Answer:
It efficiently sorts the list in O(n log n) time without needing extra space for arrays. -> Option AQuick Check:
Merge sort = O(n log n) + memory efficient [OK]
- Thinking merge sort uses extra array space for linked lists
- Confusing merge sort with bubble sort time complexity
- Assuming merge sort only works on arrays
- Believing merge sort requires integer-only lists
Solution
Step 1: Identify the standard technique to find middle in linked list
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 requires extra traversal; converting to array is inefficient; removing last node is not splitting in half.Final Answer:
Use two pointers: slow moves 1 step, fast moves 2 steps; slow marks middle. -> Option CQuick Check:
Two-pointer technique = correct split [OK]
- Trying to split by counting nodes first
- Converting linked list to array unnecessarily
- Removing nodes instead of splitting
- Using only one pointer to find middle
Solution
Step 1: Break down the list and sort halves recursively
Split into (4 -> 2) and (1 -> 3). Sort each half: (2 -> 4) and (1 -> 3).Step 2: Merge the two sorted halves
Merge (2 -> 4) and (1 -> 3) to get (1 -> 2 -> 3 -> 4).Final Answer:
1 -> 2 -> 3 -> 4 -> null -> Option DQuick Check:
Sorted ascending = 1,2,3,4 [OK]
- Not sorting halves before merging
- Merging without sorting causing wrong order
- Confusing node values during merge
- Forgetting to set last node's next to null
public ListNode MergeSort(ListNode head) {
if (head == null || head.next == null) return head;
ListNode mid = GetMiddle(head);
ListNode left = MergeSort(head);
ListNode right = MergeSort(mid);
return Merge(left, right);
}
Solution
Step 1: Identify the role of splitting the list
After finding middle, the list must be split by setting middle's previous node next to null to separate halves.Step 2: Explain the bug effect
Without disconnecting, recursive calls keep processing the same list, causing infinite recursion or stack overflow.Final Answer:
The middle node is not disconnected from the left half, causing infinite recursion. -> Option BQuick Check:
Disconnect middle to avoid infinite recursion [OK]
- Not disconnecting the list at middle
- Wrong base case conditions
- Calling merge before sorting halves
- Assuming GetMiddle returns last node
Solution
Step 1: Understand merge sort handles duplicates by sorting all nodes
Merge sort compares node values and merges in ascending order, preserving duplicates in sorted order.Step 2: Sort the list step-by-step
Sorted order is 1, 2, 2, 3, 3, so the final list is 1 -> 2 -> 2 -> 3 -> 3 -> null.Final Answer:
1 -> 2 -> 2 -> 3 -> 3 -> null -> Option AQuick Check:
Duplicates sorted ascending = 1,2,2,3,3 [OK]
- Assuming duplicates get removed
- Mixing order of duplicate nodes
- Not sorting duplicates correctly
- Confusing stable sort with unstable sort
Ready to prove it?
Pass the test and this pattern moves unknown
