Patterns/Linked List

Linked List

Top interview pattern
80 min read
Updated June 2026
What you'll learn
  • Identify which of the five core operations a new linked list problem requires and in what order to apply them
  • Apply three pointer safety rules (save before overwrite, preserve the remainder, verify exit state) that prevent the most common linked list bugs
  • Use fast/slow pointers with the correct loop condition for each problem, understanding why the termination check determines which middle node you land on
  • Solve composition problems (reorder list, palindrome check, merge sort) by combining the core operations rather than writing each from scratch

Linked list problems require managing pointer state without index-based access. The operations themselves are short, but the order of assignments matters: overwrite a pointer too early and the rest of the list becomes unreachable. The five operations in this guide cover the full problem space. Most hard problems are compositions of two or three of them.

Core Operations

A small set of operations covers the entire problem space. Every linked list question reduces to some combination of these.

BlockWhen It AppearsWhat You Track
TraverseCounting nodes, searching for a value, printingcurr
ReverseReorder, palindrome check, reverse between positionsprev, curr, next_node
Find middle (fast/slow)Split for merge sort, palindrome, reorderslow, fast
Detect cycle (Floyd)Cycle detection, find cycle startslow, fast
Merge sortedCombine two sorted lists, merge sort merge stepdummy, tail

Finding the Middle Node

Slow
node 1
Fast
node 1
Middle found
no
12345slowfast

Both pointers start at the head. The 2:1 speed ratio means when fast finishes the list, slow has covered exactly half the distance.

Key Insight
Identifying the Operations
When you see a new linked list problem, the first question is which operations it requires. Reorder List needs find middle, reverse, and interleave. Palindrome needs find middle, reverse, and compare. Reverse Nodes in k-Group needs count, reverse sublist, and reconnect, repeated. Identifying the operations determines the implementation.

What Linked List Operations Actually Do

Arrays give O(1) random access. Linked lists do not. The only way to reach any node is to follow the chain from the start. There is no index arithmetic, no slice, no backward jump. Every operation must read forward in order.

This constraint means that every pointer write is permanent until reversed. Overwrite curr.next before saving what it pointed to, and every node after curr becomes unreachable. There is no way to recover them. No error appears. The code runs to completion with a silent truncation.

Each of the five operations has a fixed order of assignments that avoids this:

  • In reversal, the forward reference is saved before the link is redirected.
  • In splitting at the middle, the second half is saved before the first half's link is severed.
  • In merging, the remainder is attached after the comparison loop exits.
  • In Reverse Between, the predecessor and successor are both stored before the segment is reversed.
  • In removal by gap, the gap is set to n+1 so the follower stops at the predecessor, not the target.

The operations are short because the rules are few. Violating the order loses nodes without producing any visible error.

The Dummy Head

Without a dummy, deleting or inserting at the head requires its own special case:

The pain: special-case code
1# Without dummy: 2 extra lines for the head case
2if head.val == target:
3 return head.next # special case!
4curr = head
5while curr.next:
6 if curr.next.val == target:
7 curr.next = curr.next.next
8 break
9 curr = curr.next
10return head

A dummy node sits before the real head. Every real node now has a predecessor, so the head is no longer a special case:

With dummy head
1# With dummy head: no special case
2dummy = ListNode(0, head)
3curr = dummy
4while curr.next:
5 if curr.next.val == target:
6 curr.next = curr.next.next
7 break
8 curr = curr.next
9return dummy.next

Pointer Safety

Three categories of pointer errors cause the majority of linked list bugs. Each one is easy to prevent once you know what to look for.

Rule 1: Save before overwrite

Before changing any .next pointer, save what it currently points to. Then change it. Then use the saved reference to keep moving forward.

Common mistake vs correct approach
1# WRONG - loses the rest of the list
2curr.next = prev # node after curr is now unreachable
3curr = curr.next # curr now equals prev (wrong direction!)
4
5# RIGHT - save first, then overwrite
6next_node = curr.next # save reference to next node
7curr.next = prev # safely redirect the pointer
8curr = next_node # advance using the saved reference

Rule 2: Never lose the remainder

Before splitting, severing, or redirecting, make sure some variable still holds a reference to the rest of the list. When you split at the middle, save second = slow.next before setting slow.next = None. When merging, attach the leftover list after the loop: tail.next = l1 if l1 else l2.

Rule 3: Know your exit state

After a loop ends, know which variable holds what. After reversal: prev is the new head, curr is None. After fast/slow: slow is the middle, fast is at or past the end. Getting this wrong means returning the wrong node or breaking reconnections.

Pattern 1: Reverse List

The most common linked list operation. Three pointers coordinate each iteration: prev tracks the last reversed node, curr is the node being processed, and next_node saves the forward reference before it gets overwritten.

Key Insight
What the Loop Maintains
After every iteration, prev heads the reversed prefix and curr heads the unreversed suffix. This holds from the first iteration to the last. When curr becomes None, the entire list has been processed and prev is the new head.
Iterative Reversal
1def reverseList(head):
2 prev = None
3 curr = head
4
5 while curr:
6 next_node = curr.next # 1. Save next
7 curr.next = prev # 2. Reverse pointer
8 prev = curr # 3. Advance prev
9 curr = next_node # 4. Advance curr
10
11 return prev # prev is the new head

Linked List Reversal

prev
null
curr
node 1
next
null
Action
INIT
12345currprevcurrnextoriginalreversed

Three pointers initialized. prev starts as null because the new tail points to nothing.

Step-by-Step Walkthrough

Trace of reversing [1 → 2 → 3 → 4 → 5] with pointer state at each step:

Stepprevcurrnext_nodeActionList State
InitNone1-Initialize pointers1→2→3→4→5
1None12Save, reverse, advanceNone←1 2→3→4→5
2123Save, reverse, advanceNone←1←2 3→4→5
3234Save, reverse, advanceNone←1←2←3 4→5
4345Save, reverse, advanceNone←1←2←3←4 5
545NoneSave, reverse, advanceNone←1←2←3←4←5
End5None-curr is None, loop exitsReturn prev (5)
Common Mistake
Forgetting to save next_node
After curr.next = prev, the original next node is gone. If you then do curr = curr.next, curr moves to prev (backward), not forward. The list after curr becomes permanently unreachable.
Common Mistake
Advancing curr with curr.next after overwrite
Even if you remember to save next_node, using curr = curr.next instead of curr = next_node after the reversal gives the same bug. Always advance with the saved reference.

Pattern 2: Reverse Between (Sublist)

Reversing a sublist uses the identical prev/curr/next reversal loop, but introduces a harder problem: once the segment is reversed, you must stitch it back into the surrounding list without breaking any links. This requires tracking three precise connection points before and after the reversal.

Key Insight
Three connection points
The predecessor (position left - 1) must point to the new segment head. The node originally at position left becomes the segment tail after reversal. And the successor (position right + 1) must be linked from that new tail. Miss any of these three reconnections and the list breaks.
Reverse Between Positions
1def reverseBetween(head, left, right):
2 dummy = ListNode(0, head)
3
4 # Step 1: Find predecessor (node before position left)
5 connection = dummy
6 for _ in range(left - 1):
7 connection = connection.next
8
9 # Step 2: Reverse the segment
10 prev = None
11 curr = connection.next # first node of segment
12 tail = curr # this becomes the tail after reversal
13
14 for _ in range(right - left + 1):
15 next_node = curr.next
16 curr.next = prev
17 prev = curr
18 curr = next_node
19
20 # Step 3: Reconnect
21 connection.next = prev # predecessor → new segment head
22 tail.next = curr # new tail → successor
23
24 return dummy.next

Reverse Between: Three-Point Reconnection

Phase 1: Find Predecessor
D12345connection
0# head = 1→2→3→4→5, left = 2, right = 4
1def reverseBetween(head, left, right):
2 dummy = ListNode(0, head)
3 connection = dummy
4 for _ in range(left - 1):
5 connection = connection.next
6 tail = connection.next
7 curr = tail
8 prev = None
9 for _ in range(right - left + 1):
10 next_node = curr.next
11 curr.next = prev
12 prev = curr
13 curr = next_node
14 connection.next = prev
15 tail.next = curr
16 return dummy.next

The goal: reverse nodes at positions 2 through 4 without disturbing the rest of the list. We need to find the node right before position 2, because that is our anchor for reconnection.

Why dummy handles left=1: When left = 1, the predecessor is position 0, which doesn't exist in the real list. The dummy sits at position 0, so connection = dummy works without a special case.

Common Mistake
Off-by-one finding the connection point
If you traverse left steps instead of left - 1, connection lands on the first node of the segment instead of its predecessor. Then connection.next = prev skips a node.
Common Mistake
Forgetting tail.next reconnection
After reversing the segment, the new tail still has .next = None (from the reversal). Without tail.next = curr, everything after position right is lost.

Pattern 3: Find Middle

slow advances one node per step, fast advances two. When fast reaches the end, slow sits at the middle. The idea is simple, but the loop condition determines which of the two middles you land on for even length lists, and that distinction matters.

Finding the Middle Node

Slow
node 1
Fast
node 1
Middle found
no
12345slowfast

Both pointers start at the head. The 2:1 speed ratio means when fast finishes the list, slow has covered exactly half the distance.

Two loop conditions exist. Choosing the wrong one causes bugs on even length lists:

Loop ConditionOn [1,2,3,4]NameUse For
while fast and fast.nextslow → node 3 (second middle)Second middleMiddle of the Linked List / when the second middle should be returned directly
while fast.next and fast.next.nextslow → node 2 (first middle)First middlePalindrome, Reorder List, Sort List (slow must be last node of first half before slow.next = None)
Second Middle (for merge sort)
1def findMiddle(head):
2 slow = head
3 fast = head
4
5 while fast and fast.next:
6 slow = slow.next
7 fast = fast.next.next
8
9 return slow # second middle for even-length lists
First Middle (for palindrome, reorder)
1def findMiddle(head):
2 slow = head
3 fast = head
4
5 while fast.next and fast.next.next:
6 slow = slow.next
7 fast = fast.next.next
8
9 return slow # first middle, last node of first half
Key Insight
First Middle vs Second Middle
Need slow to be the last node of the first half (for splitting)? Use fast.next and fast.next.next. Need slow to be the first node of the second half? Use fast and fast.next.
Common Mistake
Wrong variant for the problem
Using while fast and fast.next for palindrome check means slow overshoots on even lists. You split one node too late and the comparison gets misaligned. Test your chosen variant on [1,2,3,4] and [1,2,3,4,5] before committing.

Pattern 4: Floyd's Cycle Detection + Find Start

Floyd's algorithm solves both cycle detection and finding the cycle start in O(n) time and O(1) space. Two phases: first confirm a cycle exists, then locate where it begins.

Floyd's Cycle Detection

Slow
node 1
Fast
node 1
Phase
Detect
Status
INIT
cycle123456slowfastSlow (1 step)Fast (2 steps)

Both pointers start at the head. The speed difference of 1 node per step guarantees they will meet inside any cycle.

Step 1 of 8

Phase 1: Detect the Cycle

Fast moves 2 nodes per step, slow moves 1. If there's no cycle, fast reaches None. If there is a cycle, both pointers enter it and fast closes the gap by exactly 1 node per step. It can't skip over slow. Starting gap d means they collide in d steps.

Phase 1: Cycle Detection
1def hasCycle(head):
2 slow = head
3 fast = head
4
5 while fast and fast.next:
6 slow = slow.next
7 fast = fast.next.next
8
9 if slow == fast:
10 return True # cycle detected
11
12 return False # fast reached the end, no cycle

Phase 2: Find the Cycle Start

After the collision, reset one pointer to head and leave the other at the meeting point. Advance both at speed 1. They converge at the cycle start. The math: let a = head to cycle start, b = cycle start to meeting point, c = cycle length. Slow traveled a + b, fast traveled 2(a + b). The extra distance is full cycles: a + b = kc, so a = kc - b. Walking a steps forward from the meeting point goes c - b steps plus full laps, landing exactly at the cycle start.

Phase 2: Find Cycle Start
1def detectCycle(head):
2 # Phase 1: Detect cycle
3 slow = head
4 fast = head
5
6 while fast and fast.next:
7 slow = slow.next
8 fast = fast.next.next
9
10 if slow == fast:
11 # Phase 2: Find cycle start
12 pointer = head
13 while pointer != slow:
14 pointer = pointer.next
15 slow = slow.next
16 return pointer # cycle start
17
18 return None # no cycle
Key Insight
Two Phases of Floyd's Algorithm
Phase 1 proves a cycle exists. Phase 2 exploits the relationship a = kc - b to converge two speed 1 pointers at the cycle start. Total: O(n) time, O(1) space.

Pattern 5: Nth From End / Remove Nth

Two pointers with a fixed gap. Advance the leader n + 1 steps ahead, then move both at the same pace. When the leader reaches None, the follower is one node before the target, positioned for the deletion with follower.next = follower.next.next.

Interview Tip
Gap is n+1, not n
The gap is n+1, not n. An n-step gap would land the follower on the target itself, which is too late for deletion. You need the predecessor.
Remove Nth Node From End
1def removeNthFromEnd(head, n):
2 dummy = ListNode(0, head)
3 leader = dummy
4 follower = dummy
5
6 # Create n+1 gap
7 for _ in range(n + 1):
8 leader = leader.next
9
10 # Advance both until leader reaches None
11 while leader:
12 leader = leader.next
13 follower = follower.next
14
15 # follower.next is the target
16 follower.next = follower.next.next
17
18 return dummy.next

Nth From End: Fixed-Gap Two-Pointer

Phase
Phase 1: Create Gap
leader
D
follower
D
Gap
0
Operation
Initialize
Progress
1 / 10
D12345leaderfollower

Both pointers start at the dummy. We need a gap of n+1 (not n) because follower must land one node before the target so it can perform the deletion.

Trace for n = 2 on [1,2,3,4,5]:

StepleaderfollowerGap
Initdummydummy0
Gap 11dummy1
Gap 22dummy2
Gap 33dummy3
Advance 1413
Advance 2523
Advance 3None3-

Follower is at node 3. follower.next is node 4 (2nd from end). Delete it: follower.next = follower.next.next gives [1,2,3,5].

Common Mistake
Forgetting the dummy when n equals list length
If n equals the list length, you are removing the head. Without a dummy, follower has no predecessor to stand on. The dummy provides position 0 so the algorithm works uniformly.

Pattern 6: Merge Two Sorted Lists

This pattern shows up standalone and as the merge step inside merge sort. A dummy head starts the result, and a tail pointer tracks the end of what you've built so far. Each step: compare the two heads, append the smaller one to tail, advance that list's pointer.

Merging Sorted Lists

Comparing
1 vs 2
Selected
Result
empty
List AList BResult1352467D

Compare 1 vs 2. Both lists are sorted so the global minimum is always at one of the two heads. Take 1.

Merge Two Sorted Lists
1def mergeTwoLists(l1, l2):
2 dummy = ListNode(0)
3 tail = dummy
4
5 while l1 and l2:
6 if l1.val <= l2.val:
7 tail.next = l1
8 l1 = l1.next
9 else:
10 tail.next = l2
11 l2 = l2.next
12 tail = tail.next
13
14 # The line many forget:
15 tail.next = l1 if l1 else l2
16
17 return dummy.next
Common Mistake
Forgetting the remainder
tail.next = l1 if l1 else l2 after the loop is the most forgotten line in merge implementations. Without it, whichever list still has nodes gets silently dropped.
Interview Tip
Dummy simplifies append logic
The dummy eliminates a conditional: without it, the first append needs "if result is empty, set head" logic. With a dummy, every append is just tail.next = winner.

Compositions (Recipes)

Each problem below uses two or three of the patterns covered above. The code for each operation is already written. The work is seeing which ones apply and in what order.

Reorder List

[1→2→3→4→5][1→5→2→4→3]

Three operations: find middle (first middle variant), reverse the second half, interleave the two halves.

Reorder List: Three-Phase Decomposition

Phase
Phase 1: Find Middle
Operation
Initialize pointers
Progress
1 / 13
12345slowfast

Reorder requires three phases: find middle, reverse second half, then interleave. Start by locating the midpoint with fast/slow pointers.

Reorder List
1def reorderList(head):
2 if not head or not head.next:
3 return
4
5 # Block 1: Find middle (first middle, slow is last of first half)
6 slow, fast = head, head
7 while fast.next and fast.next.next:
8 slow = slow.next
9 fast = fast.next.next
10
11 # After finding middle, slow.next still points to the second half.
12 # Cut the link so both halves become independent lists.
13 second = slow.next
14 slow.next = None
15
16 # Block 2: Reverse second half
17 prev = None
18 curr = second
19 while curr:
20 next_node = curr.next
21 curr.next = prev
22 prev = curr
23 curr = next_node
24 second = prev
25
26 # Block 3: Interleave
27 first = head
28 while second:
29 tmp1 = first.next
30 tmp2 = second.next
31 first.next = second
32 second.next = tmp1
33 first = tmp1
34 second = tmp2
Common Mistake
Forgetting slow.next = None
Without severing the first half from the second, the first half still reaches into the second half. The interleave creates a cycle or includes nodes twice.

Palindrome Linked List

Check if a linked list reads the same forward and backward. O(n) time, O(1) space.

Three operations: find middle (first middle variant), reverse the second half, compare node by node. Optionally restore the list afterward.

Palindrome Linked List
1def isPalindrome(head):
2 if not head or not head.next:
3 return True
4
5 # Block 1: Find middle (first middle)
6 slow, fast = head, head
7 while fast.next and fast.next.next:
8 slow = slow.next
9 fast = fast.next.next
10
11 # After finding middle, slow.next still points to the second half.
12 # Cut the link so both halves become independent lists.
13 second = slow.next
14 slow.next = None
15
16 # Block 2: Reverse second half
17 prev = None
18 curr = second
19 while curr:
20 next_node = curr.next
21 curr.next = prev
22 prev = curr
23 curr = next_node
24 second = prev
25
26 # Block 3: Compare
27 p1, p2 = head, second
28 while p2:
29 if p1.val != p2.val:
30 return False
31 p1 = p1.next
32 p2 = p2.next
33
34 return True

Same slow.next = None cut as Reorder List. The compare loop uses while p2 because the second half is equal or shorter than the first on odd length lists.

Sort List

Merge sort a linked list. O(n log n) time, O(log n) space (recursion stack).

Four operations: find middle (first middle variant), split into two independent lists, recursive sort each half, merge the sorted results.

Sort List (Merge Sort)
1def sortList(head):
2 # Base case: 0 or 1 nodes
3 if not head or not head.next:
4 return head
5
6 # Block 1: Find middle and split
7 slow, fast = head, head
8 while fast.next and fast.next.next:
9 slow = slow.next
10 fast = fast.next.next
11
12 second = slow.next
13 # After finding middle, slow.next still points to the second half.
14 # Cut the link so both halves become independent lists.
15 slow.next = None
16
17 # Block 2: Recursive sort
18 left = sortList(head)
19 right = sortList(second)
20
21 # Block 3: Merge sorted halves
22 dummy = ListNode(0)
23 tail = dummy
24 while left and right:
25 if left.val <= right.val:
26 tail.next = left
27 left = left.next
28 else:
29 tail.next = right
30 right = right.next
31 tail = tail.next
32
33 tail.next = left if left else right
34 return dummy.next

The critical cut slow.next = None splits the list into two independent lists. Without it, the recursive call on head still reaches into the second half, causing infinite recursion. Base case: a single node or None is already sorted.

Key Insight
The Universal Split Pattern
All three compositions use the same first two steps: find middle, then split with slow.next = None. The third step varies. Reorder interleaves. Palindrome compares. Sort merges. Get the split right and you have most of each solution written.

When These Techniques Break

Each technique has assumptions. Knowing where they fail tells you when to reach for a different approach.

Random access is needed

If the problem requires looking up nodes by index repeatedly, a linked list is the wrong structure. Arrays give O(1) access by index. Linked list traversal is O(n) per lookup. A problem that indexes into the list frequently is better solved with an array or deque.

Backward traversal is needed repeatedly

A singly linked list only traverses forward. If the problem requires stepping backward at multiple points, a doubly linked list or a stack to store previous nodes is usually cleaner than re-traversing from the head each time.

Reversal is destructive

Iterative reversal rewires every pointer in the list. The original order no longer exists after it runs. If the list must be returned in its original state, you must reverse it a second time afterward. Problems that allow input modification (palindrome checking in most interview settings) accept the destruction. If the problem explicitly requires the input to be unchanged, reversal must be followed by a second reversal to restore order.

Recursion depth

Recursive reversal and recursive merge sort both use O(n) stack space for call frames. On very long lists, this overflows the call stack. The iterative equivalents for reversal and merge both use O(1) space and should be preferred unless recursion is specifically requested or the list is guaranteed short.

Merge sort is not truly O(1) space in the recursive form

The recursive merge sort in this guide uses O(log n) stack space for the recursive call frames. It is not strictly O(1) space. Truly constant-space merge sort requires a bottom-up iterative approach that merges fixed-size sublists in passes. This is significantly harder to implement and is rarely required in interviews, but is worth knowing the distinction if asked.

Fast/slow assumes a linear structure

The convergence proofs for cycle detection and middle-finding assume a one-directional chain with no branching. A tree or any structure where a node has multiple next pointers breaks both techniques. When working on trees, use DFS or BFS rather than the fast/slow pointer approach.

Debugging Linked List Code

When a linked list solution produces a wrong answer or crashes, work through these questions before rereading the problem. Most bugs fall into one of seven categories.

1

Did you save next_node = curr.next before setting curr.next = prev? Without this, everything past curr is permanently unreachable.

2

After finding the middle, did you set slow.next = None? Without the cut, the first half still reaches into the second half and the two lists are not independent.

3

After the merge loop, did you attach the remaining list with tail.next = l1 if l1 else l2? Whichever list runs out last still has nodes. Without this line, they are silently dropped.

4

Which fast/slow loop condition did you use? while fast and fast.next lands on the second middle; while fast.next and fast.next.next lands on the first. Trace both on a 4-node list to confirm which your problem needs.

5

After Reverse Between, did you set both connection.next = prev and tail.next = curr? Missing either reconnection severs the list at that boundary.

6

Are you returning dummy.next and not dummy or head? The dummy node is the sentinel, not part of the result.

7

After the reversal loop, what does each variable point to? prev is the new head and curr is None. If code after the loop expects otherwise, the loop exited one step early or late.

Practice Problems

Reversal

ProblemDifficultyApproach
Reverse Linked ListEasyKeep three pointers: prev, curr, next. Each step points curr.next back to prev, then shifts all three forward. Return prev when curr hits null. O(n) time, O(1) space.
Reverse Linked List IIMediumWalk to position left minus 1 using a dummy node. Reverse the sublist from left to right with the three pointer technique, then stitch the boundaries back together.
Reverse Nodes in k-GroupHardCount k nodes ahead. If fewer than k remain, leave them alone. Otherwise reverse the next k nodes as a group and recurse on the rest. Dummy node keeps the head clean.
Swap Nodes in PairsMediumUse a dummy node. For each pair, rewire the two nodes and advance the pointer by two. Stop when fewer than two nodes remain (handles odd length).

Fast-Slow

ProblemDifficultyApproach
Middle of the Linked ListEasyMove slow one step, fast two steps. When fast reaches the end, slow is at the middle. For even length lists this gives the second of the two middle nodes.
Linked List CycleEasyAdvance slow by one, fast by two. If they ever meet, there is a cycle. If fast hits null, there is no cycle.
Linked List Cycle IIMediumAfter detecting the meeting point, reset one pointer to head. Move both one step at a time. They meet at the cycle entrance. Math: the distance from head to entrance equals the distance from meeting point to entrance around the cycle.
Happy NumberEasyTreat the sequence of digit square sums as a linked list. Use fast slow to detect a cycle. If the cycle includes 1, the number is happy. Otherwise it loops forever.

Merge & Rearrange

ProblemDifficultyApproach
Merge Two Sorted ListsEasyDummy head, compare fronts, append the smaller node and advance that pointer. When one list runs out, attach the other. O(n+m) time.
Remove Nth Node From EndMediumTwo pointers with a gap of n nodes. When the lead hits the end, the trailer is right before the target. Dummy node handles removing the head.
Merge k Sorted ListsHardPush the head of each list into a min heap. Pop the smallest, append it, push its next node. Repeat until the heap is empty. O(N log k) where N is total nodes.

Compositions

ProblemDifficultyApproach
Reorder ListMediumFind middle with fast slow, reverse the second half, then interleave nodes from both halves by alternating next pointers. Three classic operations composed into one.
Palindrome Linked ListMediumFind middle, reverse second half, compare both halves node by node. Optionally restore the list afterward. O(n) time, O(1) space.
Sort ListMediumSplit at the middle using fast slow, recursively sort each half, merge with the standard merge technique. O(n log n) time, O(log n) stack space.
Add Two Numbers IIMediumReverse both lists so digits go from ones place up. Add digit by digit with carry using the standard addition loop. Reverse the result to restore original order.

Reference Templates

Use this section to recover the code shapes without rereading the full guide.

Iterative Reversal

Use when every pointer in the list must be redirected.

1def reverseList(head):
2 prev, curr = None, head
3 while curr:
4 next_node = curr.next
5 curr.next = prev
6 prev = curr
7 curr = next_node
8 return prev

Find Middle: Second Middle

Use when a problem needs the start of the second half (merge sort).

1slow, fast = head, head
2while fast and fast.next:
3 slow = slow.next
4 fast = fast.next.next
5# slow is at the second middle for even-length lists

Find Middle: First Middle

Use when the problem splits the list and needs slow as the last node of the first half (palindrome, reorder).

1slow, fast = head, head
2while fast.next and fast.next.next:
3 slow = slow.next
4 fast = fast.next.next
5# slow is at the first middle; slow.next starts the second half

Floyd's Cycle Detection + Cycle Start

Use when loop structure must be detected in O(1) space.

1slow, fast = head, head
2while fast and fast.next:
3 slow = slow.next
4 fast = fast.next.next
5 if slow == fast: # cycle detected
6 ptr = head
7 while ptr != slow:
8 ptr = ptr.next
9 slow = slow.next
10 return ptr # cycle start
11return None # no cycle

Remove Nth From End

Use when the deletion target is defined relative to the tail.

1dummy = ListNode(0, head)
2leader = follower = dummy
3for _ in range(n + 1): # n+1 gap so follower lands at predecessor
4 leader = leader.next
5while leader:
6 leader = leader.next
7 follower = follower.next
8follower.next = follower.next.next
9return dummy.next

Merge Two Sorted Lists

Use when both input lists are already sorted.

1dummy = ListNode(0)
2tail = dummy
3while l1 and l2:
4 if l1.val <= l2.val:
5 tail.next = l1; l1 = l1.next
6 else:
7 tail.next = l2; l2 = l2.next
8 tail = tail.next
9tail.next = l1 if l1 else l2 # attach remainder
10return dummy.next

Reverse Between

Use when only a bounded segment should be reversed.

1dummy = ListNode(0, head)
2connection = dummy
3for _ in range(left - 1):
4 connection = connection.next
5prev, curr = None, connection.next
6tail = curr
7for _ in range(right - left + 1):
8 next_node = curr.next
9 curr.next = prev
10 prev = curr
11 curr = next_node
12connection.next = prev # predecessor → new segment head
13tail.next = curr # new tail → successor
14return dummy.next

Pattern Recap

  • Iterative reversal uses three pointers. next_node must be saved before curr.next is overwritten. After the loop, prev is the new head and curr is None.
  • Finding the middle uses two pointers at different speeds. The loop condition determines which of the two middles is returned for even-length lists. Verify on a 4-node example before committing to a condition.
  • Floyd's detection works because fast closes the gap by exactly 1 per step once both pointers are inside the cycle. The cycle-start phase exploits the distance symmetry: the remaining distance from the meeting point to the cycle start equals the distance from head to the cycle start.
  • The n+1 gap in Nth From End positions the follower at the predecessor of the target, not the target itself. A gap of n lands one step too late for deletion.
  • Merge needs tail.next = l1 if l1 else l2 after the loop. The dummy eliminates the empty-result special case and makes every append uniform.
  • All three compositions (Reorder, Palindrome, Sort) begin with find middle and sever the first half with slow.next = None. The third step differs per problem: interleave, compare, or merge.

Rebuilding the Pattern

When a linked list problem feels unclear, start with the operations it requires. A problem that asks for ordered manipulation after splitting always begins with find middle. A problem that asks for structure detection without extra space is almost always fast/slow. A problem that involves sorted output from a linked list is merge or merge sort. Identifying the required operations first makes the implementation straightforward because each operation has a fixed, short template.

After identifying the operations, check pointer safety for each one. Every bug in a linked list solution comes from one of three sources: overwriting a pointer before saving the forward reference, failing to sever the link when splitting a list into two independent halves, or losing the remainder when a merge loop exits before one list is exhausted. Getting those three checks right in the correct order is the whole skill.

Check Your Understanding

Use these questions to check whether you can reason through the operations without looking at the template.

Ready to test it?

12 questions across reversal, fast-slow, cycle detection, and merging.

Practice this pattern

Apply the guide to complete interview problems with explanations and code.

Learn Linked Lists in a guided sequence

The Interview Course connects this pattern to its prerequisites, worked lessons, and progressively harder problems.

Explore the course