💡 Merge compares nodes stepwise to maintain sorted order.
traverse
Advance tail pointer after final merge
Move 'tail' pointer to the end of the merged list (node with value 4).
💡 Tail points to the last node of the sorted list.
Line:while tail.next:
tail = tail.next
💡 Tail marks the end for any further merges (none left here).
expand
Double size to 4, which equals list length; end merging
Double sublist size to 4, which is equal to list length, so sorting is complete.
💡 When size >= length, the entire list is sorted.
Line:size <<= 1
💡 Doubling size beyond length ends the merge passes.
reconstruct
Return sorted list starting at dummy.next
Return the sorted list starting at dummy.next, which is node with value 1.
💡 The dummy node's next pointer now points to the fully sorted list.
Line:return dummy.next
💡 Sorted list head is returned for use.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def merge(l1, l2):
dummy = ListNode(0) # STEP 6,10,16
tail = dummy
while l1 and l2:
if l1.val < l2.val:
tail.next = l1
l1 = l1.next
else:
tail.next = l2
l2 = l2.next
tail = tail.next
tail.next = l1 or l2
return dummy.next
def split(head, size):
for i in range(size - 1): # STEP 4,5,8,9,14,15
if head:
head = head.next
else:
break
if not head:
return None
second = head.next
head.next = None
return second
def sortList(head):
if not head or not head.next:
return head
length = 0
node = head
while node: # STEP 1
length += 1
node = node.next
dummy = ListNode(0, head) # STEP 2
size = 1
while size < length: # STEP 12,18
cur = dummy.next # STEP 3,13
tail = dummy
while cur:
left = cur
right = split(left, size) # STEP 4,5,8,9,14,15
cur = split(right, size)
merged = merge(left, right) # STEP 6,10,16
tail.next = merged
while tail.next:
tail = tail.next # STEP 7,11,17
size <<= 1
return dummy.next # STEP 19
📊
Sort List (Merge Sort on LL) - Watch the Algorithm Execute, Step by Step
Watching each pointer move and merge operation helps you understand how merge sort works on linked lists without extra space.
Step 1/19
·Active fill★Answer cell
traverse
4
→
2
→
1
→
3
Result: 4
setup
0
→
4
→
2
→
1
→
3
setup
0
→
4
→
2
→
1
→
3
split
0
→
4
→
2
→
1
→
3
split
0
→
4
→
2
→
1
→
3
merge
0
→
4
→
2
→
1
→
3
traverse
0
→
4
→
2
→
1
→
3
split
0
→
4
→
2
→
1
→
3
split
0
→
4
→
2
→
1
→
3
merge
0
→
4
→
2
→
1
→
3
traverse
0
→
4
→
2
→
1
→
3
expand
0
→
4
→
2
→
1
→
3
setup
0
→
4
→
2
→
1
→
3
split
0
→
4
→
2
→
1
→
3
split
0
→
4
→
2
→
1
→
3
merge
0
→
4
→
2
→
1
→
3
traverse
0
→
4
→
2
→
1
→
3
expand
0
→
4
→
2
→
1
→
3
reconstruct
1
→
2
→
3
→
4
Result: [1, 2, 3, 4]
Key Takeaways
✓ Merge sort on linked lists works by splitting the list into smaller sublists and merging them in sorted order.
This is hard to see from code alone because the pointer manipulations and sublist boundaries are implicit.
✓ Using a dummy node simplifies merging by providing a stable anchor point for the sorted list.
Without visualization, it's not obvious how dummy helps avoid special cases for head changes.
✓ Doubling the sublist size each pass reduces the number of merges and leads to a fully sorted list efficiently.
The iterative size doubling is a key optimization that is clearer when watching the passes visually.
Practice
(1/5)
1. The following code attempts to merge k sorted linked lists using a min-heap. Identify the line that contains a subtle bug that can cause incorrect merging or runtime errors.
medium
A. Line: current.next = smallest
B. Line: Missing 'current = current.next' after attaching smallest
C. Line: while heap:
D. Line: if node: heapq.heappush(heap, node)
Solution
Step 1: Review pointer updates in loop
After attaching smallest to current.next, current must move forward.
Step 2: Identify missing line
Code lacks 'current = current.next', so current stays, causing incorrect list linking.
Final Answer:
Option B -> Option B
Quick Check:
Without advancing current, merged list nodes overwrite each other [OK]
Hint: Always advance current pointer after attaching a node [OK]
Common Mistakes:
Forgetting to move current pointer
Misplacing heap push conditions
Confusing heap pop with list traversal
2. Examine the following buggy code snippet for checking if a linked list is a palindrome using the recursive two-pointer technique. Which line contains the subtle bug that causes incorrect results on empty or single-node lists?
medium
A. Line: if not self._recursively_check(current.next):
B. Line: if current.next is not None:
C. Line: if self.front.val != current.val:
D. Line: self.front = self.front.next
Solution
Step 1: Analyze base case condition
The original buggy code used if current.next is not None: which fails when current is None (empty list) causing AttributeError.
Step 2: Correct base case check
The correct check should be if current is not None: to handle empty and single-node lists safely.
Final Answer:
Option B -> Option B
Quick Check:
Incorrect base case leads to exceptions or wrong results on edge cases [OK]
Hint: Base case must check current, not current.next [OK]
Common Mistakes:
Using current.next in base case
Forgetting to handle empty list
Moving front pointer too early
3. Consider the following buggy code snippet for removing the nth node from the end of a linked list. Which line contains the subtle bug that causes incorrect behavior when removing the head node?
medium
A. Line 13: Wrong condition for node removal
B. Line 10: Incorrect base case for recursion
C. Line 5: Missing dummy node initialization
D. Line 15: Returning head instead of dummy.next
Solution
Step 1: Identify missing dummy node
Without a dummy node, removing the head node causes pointer errors or incorrect list head.
Step 2: Check recursion call and return
Recursion is called on head, but no dummy means no safe predecessor to update head pointer.
Final Answer:
Option C -> Option C
Quick Check:
Dummy node is essential to handle head removal safely [OK]
Hint: Always use dummy node to handle head removal [OK]
Common Mistakes:
Not using dummy node
Incorrect recursion base case
Returning wrong node after removal
4. What is the time complexity of the optimal iterative approach that reverses nodes in k-groups in a singly linked list of length n? Assume k is a fixed positive integer.
medium
A. O(n log k)
B. O(n * k)
C. O(n / k * k^2)
D. O(n)
Solution
Step 1: Identify the number of groups processed
The list of length n is divided into approximately n/k groups.
Step 2: Analyze reversal cost per group
Each group reversal takes O(k) time, so total time is (n/k) * k = O(n).
Final Answer:
Option D -> Option D
Quick Check:
Each node is visited and reversed exactly once [OK]
Hint: Reversing fixed-size groups leads to linear time [OK]
Common Mistakes:
Mistaking O(n * k) by multiplying n and k without division
Confusing group count and reversal cost per group
5. The following code attempts to rotate a linked list to the right by k places. Identify the line that contains a subtle bug that can cause the list to become cyclic or truncated incorrectly.