💡 Pointer adjustment removes the target node without needing a previous pointer explicitly.
prune
Backtrack from node 2: index = 4
Returning from node 2, index increments to 4. No removal needed, recursion continues returning.
💡 Counting continues but removal already done.
Line:return idx
💡 Backtracking continues to unwind recursion stack.
prune
Backtrack from node 1: index = 5
Returning from node 1, index increments to 5. No removal needed, recursion continues returning.
💡 Unwinding recursion stack back to dummy node.
Line:return idx
💡 Backtracking completes with no further changes.
prune
Backtrack from dummy node: recursion ends
Returning from dummy node, recursion ends and the modified list head is returned.
💡 Returning dummy.next gives the new head of the updated list.
Line:return dummy.next
💡 The dummy node allows returning the updated list head easily.
reconstruct
Final list after removal
The final linked list is returned starting from node 1, showing the list with node 4 removed.
💡 The list now correctly excludes the 2nd node from the end.
Line:return dummy.next
💡 The algorithm successfully removed the target node by pointer adjustment during backtracking.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
self.n = n
def recurse(node): # STEP 2-14
if not node: # STEP 8
return 0
idx = recurse(node.next) + 1 # STEP 3-7,9-13
if idx == self.n + 1: # STEP 11
node.next = node.next.next # STEP 11
return idx
dummy = ListNode(0, head) # STEP 1
recurse(dummy) # STEP 2
return dummy.next # STEP 14-15
📊
Remove Nth Node From End of List - Watch the Algorithm Execute, Step by Step
Watching each recursive call and return helps you understand how recursion can simulate reverse traversal in a singly linked list, which is otherwise hard to visualize.
Step 1/15
·Active fill★Answer cell
connect
0
→
1
→
2
→
3
→
4
→
5
advance
0
→
1
→
2
→
3
→
4
→
5
advance
0
→
1
→
2
→
3
→
4
→
5
advance
0
→
1
→
2
→
3
→
4
→
5
advance
0
→
1
→
2
→
3
→
4
→
5
advance
0
→
1
→
2
→
3
→
4
→
5
advance
0
→
1
→
2
→
3
→
4
→
5
advance
0
→
1
→
2
→
3
→
4
→
5
Result: 0
compare
0
→
1
→
2
→
3
→
4
→
5
Result: 1
compare
0
→
1
→
2
→
3
→
4
→
5
Result: 2
connect
0
→
1
→
2
→
3
→
4
→
5
Result: 3
advance
0
→
1
→
2
→
3
→
4
→
5
Result: 4
advance
0
→
1
→
2
→
3
→
4
→
5
Result: 5
advance
0
→
1
→
2
→
3
→
4
→
5
reconstruct
1
→
2
→
3
→
5
Result: [1, 2, 3, 5]
Key Takeaways
✓ Recursion can simulate reverse traversal in a singly linked list by backtracking from the end.
This is hard to see from code alone because recursion hides the call stack and the order of execution.
✓ Using a dummy node simplifies edge cases and pointer adjustments when removing nodes.
Visualizing the dummy node clarifies why it is used and how it anchors the list.
✓ The node removal happens by adjusting the 'next' pointer of the node before the target during backtracking.
Seeing the pointer change in the visualization makes it clear how the node is removed without explicit previous pointers.
Practice
(1/5)
1. Consider the following Python code implementing the optimized fast-slow pointer approach for detecting a cycle in a circular array. Given the input nums = [2, -1, 1, 2, 2], what is the return value of the function circularArrayLoop(nums)?
easy
A. Raises an IndexError
B. false
C. true
D. Infinite loop
Solution
Step 1: Trace first iteration with i=0
Start at index 0, direction is positive. Moves: 0->2->3->0 forms a cycle of length >1 with consistent direction.
Step 2: Detect cycle and return true
Fast and slow pointers meet at index 0, cycle length >1, so function returns true.
4. Examine the following code snippet intended to detect the start of a cycle in a linked list. Identify the line containing the subtle bug that can cause a runtime error or infinite loop.
medium
A. Line 5: fast = fast.next.next
B. Line 3: while fast:
C. Line 7: if slow == fast:
D. Line 11: while ptr1 != ptr2:
Solution
Step 1: Check loop condition safety
The loop condition only checks if fast is not None, but fast.next may be None, so fast.next.next can cause an exception.
Step 2: Identify fix
The loop condition should check both fast and fast.next to avoid null pointer exceptions.
Final Answer:
Option B -> Option B
Quick Check:
Accessing fast.next.next without checking fast.next causes runtime error [OK]
Hint: Always check fast and fast.next before accessing fast.next.next [OK]
Common Mistakes:
Missing fast.next check
Returning meeting point as cycle start
Infinite loop due to wrong loop condition
5. Suppose you want to find the middle node of a linked list, but the list is circular (the last node points back to the head). Which modification to the two-pointer approach correctly finds the middle node without infinite looping?
hard
A. Use the same two-pointer approach but add a visited set to detect cycles and stop when fast pointer revisits a node.
B. Use recursion to count nodes until the head is reached again, then find middle by index.
C. Convert the circular list to a linear list by breaking the cycle first, then apply the standard two-pointer approach.
D. Modify the loop to stop when fast or fast.next equals the head node, then return slow pointer.
Solution
Step 1: Understand circular list behavior
In a circular list, fast pointer will loop infinitely unless we detect when it cycles back to head.
Step 2: Modify loop condition
Stop when fast or fast.next equals head to avoid infinite loop; slow pointer will be at middle.
Step 3: Compare alternatives
Visited set adds extra space; breaking cycle modifies input; recursion risks stack overflow.
Final Answer:
Option D -> Option D
Quick Check:
Stopping at head detects cycle end without extra space [OK]
Hint: Detect cycle by checking if fast pointer returns to head [OK]
Common Mistakes:
Using visited set wastes space
Breaking cycle modifies input
Recursion risks stack overflow
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.