Before writing a single line of code, break the problem into micro-operations. Every linked list problem reduces to some combination of: traverse, rewire, insert, delete, split, merge.
Step-by-step framework
Draw it out. Sketch 4–5 nodes. Label head, prev, curr, next, or whatever pointers the problem needs. You will misfire on pointer order if you skip this.
Identify the invariant. What pointer relationships must stay true after every iteration?
Reversal → prev always points to the already-reversed portion.
Fast/slow → fast is always 2× ahead of slow.
Determine your anchors. Which pointers move and which stay fixed?
Classic reversal: all three (prev, curr, next) walk forward.
Head-insertion reversal: prev and curr are fixed; only the splice target moves.
Handle edges first. Before the main loop, account for:
head is None
Single node
Operation at head (use a dummy/sentinel node to avoid special-casing)
Write the loop body in CUT → LINK → MOVE order. Every rewiring step is one of:
CUT — detach a node from its current position
LINK — point it to its new neighbor
MOVE — advance your working pointer(s)
Common Pitfall
If you update curr.next before saving a reference to the next node, you lose the rest of the list. Always stash your forward reference first.
Classic Three-Pointer Reversal
The bread-and-butter. Reverses an entire linked list (or a segment if you set boundaries). Walk three pointers — prev, curr, next — through the list. At each step, flip curr.next to point backward.
Template
def reverseList(head): prev = None curr = head while curr: next_node = curr.next # 1. STASH forward ref curr.next = prev # 2. FLIP the pointer prev = curr # 3. ADVANCE prev curr = next_node # 4. ADVANCE curr return prev # prev is the new head
Head-Insertion Reversal (Sublist Splicing)
Also known as
In-place sublist reversal by front insertion
Head-insertion method for reversing a segment
Iterative sublist reversal / node splicing
This is the technique to reach for when you need to reverse a segment of the list in-place without touching the rest.
Core insight
You keep two anchors fixed:
Pointer
Role
Moves?
prev
The node before the segment
❌ Stays fixed
curr
The first node of the segment (becomes the tail after reversal)
❌ Stays fixed
Then you repeatedly extract curr.next and splice it to the front of the segment (right after prev).
Template
def reverseBetween(head, left, right): dummy = ListNode(0, head) prev = dummy # 1. Walk prev to the node just before position `left` for _ in range(left - 1): prev = prev.next curr = prev.next # first node of the segment (won't move) # 2. Perform (right - left) front-insertions for _ in range(right - left): x = curr.next # CUT target curr.next = x.next # CUT it out of the chain x.next = prev.next # LINK it to the current front of the segment prev.next = x # MOVE the segment front pointer to x return dummy.next
Why curr never moves
curr is always the original first node of the segment. As new nodes get spliced in front of it, curr naturally drifts to the tail — exactly where it should end up. Meanwhile, curr.next always points to the next node to extract.
Mnemonic: CUT → LINK → MOVE
x = curr.next # CUT — grab the target
curr.next = x.next # CUT — snip it out
x.next = prev.next # LINK — wire it to the front
prev.next = x # MOVE — update the front
When to pick this over the classic reversal
Reverse Linked List II (LC 92) — reverse between positions
Reverse Nodes in k-Group (LC 25) — apply this in a loop
Any time the reversal is a sub-operation inside a bigger problem
When you want the connecting nodes (prev, tail after curr) to stay wired correctly with zero fixup
Fast & Slow Pointers
Two pointers moving at different speeds through the list. The asymmetry is what gives you information.
When to use
Problem
Fast speed
Slow speed
What you learn
Detect cycle
2 steps
1 step
If they meet → cycle exists
Find cycle start
1 step (after meeting)
1 step (from head)
Meeting point = cycle entry
Find middle
2 steps
1 step
When fast hits end, slow is at middle
Kth from end
start k steps ahead, then 1 step
1 step
When fast hits end, slow is kth from end
Palindrome check
2 steps (find middle)
1 step
Split at middle, reverse second half, compare
Template — cycle detection (Floyd’s Algorithm)
def hasCycle(head): slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next if slow == fast: return True return False
Template — find cycle start
def detectCycle(head): slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next if slow == fast: # Phase 2: find entry point slow = head while slow != fast: slow = slow.next fast = fast.next return slow # cycle start return None
Why does phase 2 work?
Let the distance from head to cycle start be a, and the distance from cycle start to the meeting point be b. At the meeting point, slow traveled a + b and fast traveled 2(a + b). The extra distance fast covered is full cycle laps. Resetting slow to head and moving both at speed 1 means they converge at exactly the cycle start after a steps.
Template — find middle
def findMiddle(head): slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next return slow # middle (right-middle for even-length)
Left-middle vs right-middle
The template above gives the right-middle for even-length lists (1→2→3→4 returns 3). If you need the left-middle, use while fast.next and fast.next.next instead.
Template — remove nth from end
def removeNthFromEnd(head, n): dummy = ListNode(0, head) fast = slow = dummy # Advance fast by n+1 so the gap is n for _ in range(n + 1): fast = fast.next while fast: slow = slow.next fast = fast.next slow.next = slow.next.next # skip the target return dummy.next
Quick Reference — Dummy Node Trick
Use a sentinel / dummy node whenever the head might change
dummy = ListNode(0, head)# ... do your work ...return dummy.next
This eliminates every if not head or if curr == head edge case. Use it liberally — there’s no cost.
Pattern Cheat Sheet
Pattern
Key Pointers
Fixed or Walking?
Core Problems
Classic reversal
prev, curr, next
All walk
LC 206, 234
Head-insertion splice
prev, curr, x
prev & curr fixed
LC 25, 92
Fast/slow — cycle
slow, fast
Both walk (different speeds)
LC 141, 142
Fast/slow — middle
slow, fast
Both walk
LC 876, 143, 234
Fast/slow — kth from end
slow, fast
Both walk (staggered start)
LC 19, 61
Merge two lists
p1, p2, dummy
p1, p2 walk
LC 21, 23, 148
Dummy sentinel
dummy
Fixed
Almost every problem
Parting thought
Every linked list problem is just pointer plumbing. If you can draw the before-and-after state of 4 nodes and write the rewiring steps in order, you can solve it. The patterns above cover ~90% of what LeetCode throws at you.