Linked list problems are pointer manipulation problems. Most solutions reduce to a few reusable patterns.
Core Patterns
- fast/slow pointers
- in-place reversal
- dummy head for edge-safe insert/delete
- merge of sorted lists
Pattern 1: Fast-Slow Pointer (Middle)
What we are doing actually:
- Move
fasttwo steps at a time. - Move
slowone step at a time. - When
fastreaches the end,slowis at the middle.
public ListNode middleNode(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next; // Moves one node per iteration.
fast = fast.next.next; // Moves two nodes per iteration.
}
return slow;
}
Debug steps:
- trace
slowandfastvalues on a 1-node, 2-node, and 5-node list - verify the loop checks both
fastandfast.next - remember even-length lists return the second middle in this version
Pattern 2: Iterative Reversal
What we are doing actually:
- Save
nextbefore breaking the current link. - Reverse the pointer direction for
cur. - Advance
prevandcur.
public ListNode reverseList(ListNode head) {
ListNode prev = null, cur = head;
while (cur != null) {
ListNode next = cur.next; // Save remainder before rewiring.
cur.next = prev; // Reverse current pointer.
prev = cur; // Grow reversed prefix.
cur = next; // Continue with unreversed remainder.
}
return prev;
}
Debug steps:
- print
prev,cur, andnextat each step - verify no node becomes unreachable after
cur.next = prev - test empty list and single-node list first
Problem 1: Linked List Cycle
Problem description: Detect whether the linked list contains a cycle.
What we are doing actually:
- Move
slowby one andfastby two. - If there is a cycle,
fasteventually lapsslow. - If
fasthitsnull, the list is acyclic.
public boolean hasCycle(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) return true;
}
return false;
}
Debug steps:
- trace
slowandfaston a tiny cyclic and acyclic list - verify comparison is by node reference, not value
- test
head = nulland one-node self-cycle
Problem 2: Merge Two Sorted Lists
Problem description: Merge two already sorted linked lists into one sorted result.
What we are doing actually:
- Use a dummy head to simplify edge cases.
- Always attach the smaller current node.
- Move the pointer in the list that contributed the node.
- Append the remaining tail at the end.
public ListNode mergeTwoLists(ListNode a, ListNode b) {
ListNode dummy = new ListNode(0), tail = dummy;
while (a != null && b != null) {
if (a.val <= b.val) {
tail.next = a; // Next smallest node comes from list a.
a = a.next;
} else {
tail.next = b; // Next smallest node comes from list b.
b = b.next;
}
tail = tail.next; // Advance merged tail.
}
tail.next = (a != null) ? a : b; // Attach whichever list still has nodes.
return dummy.next;
}
Debug steps:
- print the merged list after each append
- verify
tailalways points to the last merged node - test when one input list is empty
Problem 3: Remove Nth Node From End
Problem description: Remove the nth node from the end in one pass.
What we are doing actually:
- Add a dummy node so removing the head is easy.
- Move
firstahead byn + 1steps to create a fixed gap. - Move both pointers together until
firstreaches the end. second.nextis then the node we must remove.
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0, head);
ListNode first = dummy, second = dummy;
for (int i = 0; i <= n; i++) first = first.next; // Maintain a gap of n nodes between pointers.
while (first != null) {
first = first.next;
second = second.next;
}
second.next = second.next.next; // Skip the target node.
return dummy.next;
}
Debug steps:
- print
firstandsecondduring gap creation and synchronized movement - test removing the head, tail, and a middle node
- verify why the dummy node makes head removal safe
Edge note: this assumes n is valid (1 <= n <= length).
If input can be invalid, guard against first == null during initial gap advance.
Pointer Debug Strategy
For complex linked-list edits, debug with tiny lists and snapshot states:
prev=2 cur=3 next=4
After each mutation (cur.next = prev etc.), verify:
- no node is orphaned unexpectedly
- no accidental cycles are created
- termination pointer eventually reaches
null
Short traces catch most pointer bugs quickly.
Interview-Grade Invariant Habit
Before coding, define one invariant sentence, e.g.:
- reversal: “
previs head of reversed prefix” - merge: “
tailis last node of sorted merged list” - fast/slow: “
slowadvances half speed offast”
This reduces trial-and-error pointer updates.
Common Mistakes
- Losing next pointer during reversal
- Not using dummy node for head-edge deletes
- Null checks missing in fast/slow loops
- Mixing node references after mutation
Practice Set (Recommended Order)
- Middle of the Linked List (LC 876)
LeetCode - Reverse Linked List (LC 206)
LeetCode - Linked List Cycle (LC 141)
LeetCode - Remove Nth Node From End (LC 19)
LeetCode - Merge Two Sorted Lists (LC 21)
LeetCode - Reorder List (LC 143)
LeetCode
Key Takeaways
- Linked list mastery is pointer-state mastery.
- Dummy nodes and clear invariants prevent most bugs.
- Fast/slow and reversal are foundational for many advanced problems.
Comments