Linked List Techniques — The Complete Guide
Master linked list manipulation from first principles. Learn pointer rewiring, reversal, merging, cycle detection, and confidently solve interview questions involving linked lists.
Table of Contents
Intuition from Scratch
Why does this data structure exist?
An array stores elements in a contiguous block of memory. That's great for random access, but terrible when you need to insert or delete in the middle — you have to shift everything over. A linked list solves this by storing each element in its own node, with a pointer to the next node. Insertions and deletions become O(1) if you already have a reference to the right spot.
The trade-off? You lose random access. To reach the 5th element, you must walk through nodes 1 → 2 → 3 → 4 → 5. No shortcuts. This is why linked list problems are really about pointer manipulation — rewiring connections between nodes to achieve the desired structure.
Analogies to Build Intuition
A Train of Carriages
Each carriage (node) holds passengers (data) and has a coupling (pointer) to the next carriage. To add a carriage in the middle, you just uncouple two carriages and couple the new one between them. No need to move any other carriage.
A Chain of Paper Clips
Each paper clip hooks into the next one. To remove a clip from the middle, you unhook it and connect its neighbors directly. To reverse the chain, you flip each connection one by one. That's exactly how linked list reversal works.
A Treasure Hunt
Each clue tells you where the next clue is. You can't jump to clue #7 directly — you must follow the trail from the start. If someone changes where a clue points, the entire path after it changes. That's pointer rewiring.
What kind of problems does it solve?
Linked list techniques are your go-to when:
- You need to reverse all or part of a sequence in-place
- You need to merge two sorted sequences without extra space
- You need to detect or remove cycles in a structure
- You need to find the middle, kth-from-end, or intersection in one pass
- You need to rearrange nodes (partition, reorder, rotate) without copying data
- The problem explicitly gives you a ListNode definition
The ListNode — Your Building Block
class ListNode { val: number; next: ListNode | null; constructor(val: number = 0, next: ListNode | null = null) { this.val = val; this.next = next; } } // A linked list: 1 → 2 → 3 → null // node1.val = 1, node1.next = node2 // node2.val = 2, node2.next = node3 // node3.val = 3, node3.next = null ← end of list
The core insight
Linked list problems aren't about the data — they're about the pointers. Every operation (reverse, merge, detect cycle, find middle) is just a specific pattern of reading and rewriting .next pointers. Master the pointer patterns and you master linked lists.
Pattern Recognition
Recognizing when a problem is a linked list problem is usually obvious (they give you a ListNode). The real skill is recognizing which linked list technique to apply.
Keywords & Signals in the Problem Statement
Technique signals to look for...
- ✅"Reverse a linked list" → Iterative 3-pointer reversal
- ✅"Middle of linked list" → Fast & slow pointers
- ✅"Detect cycle" or "find cycle start" → Floyd's algorithm
- ✅"Merge two sorted lists" → Dummy head + comparison walk
- ✅"Remove nth node from end" → Two pointers with n-gap
- ✅"Palindrome linked list" → Find middle + reverse second half
- ✅"Intersection of two lists" → Length difference or two-pass trick
- ✅"Reorder / rearrange nodes" → Split + reverse + merge
- ✅"Flatten" a multi-level list → Recursion or stack
- ✅"Sort a linked list" → Merge sort (not quicksort)
Don't use linked list techniques when...
- ❌You need random access by index (use an array)
- ❌You need binary search (linked lists don't support O(1) indexing)
- ❌The problem involves contiguous subarrays (use sliding window)
- ❌You need to frequently access elements by position (use array/deque)
- ❌The data is given as an array and doesn't need pointer manipulation
- ❌You need cache-friendly traversal for performance (arrays win here)
Linked List vs. Array — When to Use Which
| Operation | Array | Linked List |
|---|---|---|
| Access by index | O(1) ✅ | O(n) ❌ |
| Insert/delete at head | O(n) — shift everything | O(1) ✅ |
| Insert/delete at middle (with ref) | O(n) — shift elements | O(1) ✅ |
| Search for value | O(n) or O(log n) if sorted | O(n) always |
| Memory overhead | None (contiguous) | Extra pointer per node |
| Cache performance | Excellent (contiguous) | Poor (scattered memory) |
The Dummy Head Trick — Know This Before Anything Else
Many linked list problems require building a new list or modifying the head. A dummy node placed before the real head eliminates all edge cases around "what if the head changes?"
function buildOrModifyList(head: ListNode | null): ListNode | null { const dummy = new ListNode(0); // dummy node before the real head dummy.next = head; let curr = dummy; // ... do your work using curr ... return dummy.next; // the real head (might have changed) } // Why: Without dummy, you'd need special-case logic for // "what if I need to remove the head?" or "what if the list is empty?"
The #1 linked list tip
When in doubt, use a dummy head. It eliminates 80% of edge cases (empty list, single node, head removal) and makes your code cleaner. Almost every linked list solution in interviews benefits from it.
Brute Force → Optimized Thinking
Let's walk through the thinking evolution using a classic example: Reverse a Linked List — given the head of a singly linked list, reverse it and return the new head.
Brute Force — Copy to Array, Reverse, Rebuild
Walk the list, copy all values into an array, reverse the array, then build a new linked list from the reversed values. This works but uses O(n) extra space and doesn't manipulate pointers at all.
function reverseList(head: ListNode | null): ListNode | null { const values: number[] = []; let curr = head; while (curr) { values.push(curr.val); curr = curr.next; } values.reverse(); // Rebuild the list from reversed values const dummy = new ListNode(0); curr = dummy; for (const val of values) { curr.next = new ListNode(val); curr = curr.next; } return dummy.next; } // Problem: O(n) extra space. We're not even using the linked list's // strength — we should be rewiring pointers, not copying data.
Better — Recursive Reversal
Recursively reverse the rest of the list, then fix the pointer of the current node. Elegant but uses O(n) stack space due to recursion depth.
function reverseList(head: ListNode | null): ListNode | null { // Base case: empty list or single node if (!head || !head.next) return head; // Reverse everything after head const newHead = reverseList(head.next); // head.next is now the LAST node of the reversed portion // Make it point back to head head.next.next = head; head.next = null; // head is now the tail return newHead; } // Elegant, but O(n) stack space. Can we do O(1) space?
Optimal — Iterative 3-Pointer Reversal
Use three pointers: prev, curr, and next. At each step, save the next node, reverse the current pointer, then advance. This is the gold standard for linked list reversal — O(1) space, single pass.
function reverseList(head: ListNode | null): ListNode | null { let prev: ListNode | null = null; let curr = head; while (curr) { const next = curr.next; // 1. Save next node curr.next = prev; // 2. Reverse the pointer prev = curr; // 3. Advance prev curr = next; // 4. Advance curr } return prev; // prev is now the new head } // Perfect: O(n) time, O(1) space. Single pass, no extra memory.
Why Does Each Optimization Work?
Brute → Recursive
We realized we don't need to copy data — we can reverse the pointers themselves. The recursive approach does this by reversing the tail first, then fixing each pointer on the way back up the call stack.
Recursive → Iterative
We realized the recursion is just processing nodes one at a time — we can do the same thing with a loop and three pointers. This eliminates the O(n) call stack overhead.
The General Principle
Linked list optimizations almost always follow this pattern: (1) Don't copy data — rewire pointers. (2) Don't use recursion if iteration works — save stack space. (3) Use a constant number of pointer variables to track your position.
The thinking process matters more than the code
In interviews, showing this brute → recursive → iterative progression demonstrates deep understanding. Even if you jump to the iterative solution, mentioning "I could do this recursively but it uses O(n) stack space, so let me use the iterative approach" scores points.
Core Templates
Linked list problems boil down to a handful of reusable templates. Memorize these — they cover 90% of interview questions.
Template 1: Iterative Reversal (3-Pointer)
The most fundamental linked list operation. Used directly in reversal problems and as a building block for palindrome checks, reorder list, and more.
function reverse(head: ListNode | null): ListNode | null { let prev: ListNode | null = null; let curr = head; while (curr) { const next = curr.next; // save curr.next = prev; // reverse prev = curr; // advance prev curr = next; // advance curr } return prev; // new head } // Variations: // - Reverse between positions m and n (save connection points) // - Reverse in groups of k (reverse k nodes, recurse/loop for rest)
Template 2: Fast & Slow Pointers
Two pointers moving at different speeds. Fast moves 2 steps, slow moves 1. Used for finding the middle, detecting cycles, and finding cycle entry points.
// Find middle node (for even-length, returns first middle) function findMiddle(head: ListNode | null): ListNode | null { let slow = head; let fast = head; while (fast?.next?.next) { // stops slow at first middle slow = slow!.next; fast = fast.next.next; } return slow; } // Detect cycle (Floyd's algorithm) function hasCycle(head: ListNode | null): boolean { let slow = head; let fast = head; while (fast?.next) { slow = slow!.next; fast = fast.next.next; if (slow === fast) return true; // they met → cycle exists } return false; // fast reached end → no cycle } // Find cycle entry point function detectCycleStart(head: ListNode | null): ListNode | null { let slow = head, fast = head; while (fast?.next) { slow = slow!.next; fast = fast.next.next; if (slow === fast) break; } if (!fast?.next) return null; // no cycle slow = head; // reset slow to head while (slow !== fast) { slow = slow!.next; fast = fast!.next; // both move at speed 1 now } return slow; // meeting point = cycle start }
Template 3: Merge Two Sorted Lists (Dummy Head)
Compare heads of two sorted lists, pick the smaller one, advance that pointer. The dummy head eliminates edge cases around which list starts first.
function mergeTwoLists( l1: ListNode | null, l2: ListNode | null ): ListNode | null { const dummy = new ListNode(0); let tail = dummy; while (l1 && 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 ?? l2; // attach remaining nodes return dummy.next; } // Time: O(n + m) | Space: O(1) — we reuse existing nodes
Template 4: Two-Pointer Gap (Nth from End)
Advance the first pointer n steps ahead, then move both pointers together. When the first reaches the end, the second is at the target position.
function removeNthFromEnd( head: ListNode | null, n: number ): ListNode | null { const dummy = new ListNode(0, head); let first: ListNode | null = dummy; let second: ListNode | null = dummy; // Advance first by n+1 steps (so second lands BEFORE the target) for (let i = 0; i <= n; i++) { first = first!.next; } // Move both until first reaches the end while (first) { first = first.next; second = second!.next; } // second is now right before the node to remove second!.next = second!.next!.next; return dummy.next; } // Why n+1? Because we need second to stop ONE node BEFORE the target // so we can rewire second.next to skip the target.
Which template to pick?
Ask yourself: (1) Do I need to reverse all or part of the list? → Template 1. (2) Do I need the middle, or to detect a cycle? → Template 2. (3) Am I merging or comparing two lists? → Template 3. (4) Do I need the kth node from the end? → Template 4. Many problems combine 2+ templates (e.g., Palindrome = find middle + reverse + compare).
Variations & Sub-Patterns
Linked list techniques form a family of related patterns. Here are the most common variations and how the approach changes for each.
| Variation | Core Technique | Classic Example |
|---|---|---|
| Full Reversal | 3-pointer iterative reversal on entire list | Reverse Linked List (LC 206) |
| Partial Reversal | Reverse between positions m and n, reconnect boundaries | Reverse Linked List II (LC 92) |
| Reverse in K-Groups | Reverse k nodes at a time, chain groups together | Reverse Nodes in k-Group (LC 25) |
| Cycle Detection | Fast & slow pointers — if they meet, cycle exists | Linked List Cycle (LC 141) |
| Cycle Entry Point | After detection, reset one pointer to head, walk both at speed 1 | Linked List Cycle II (LC 142) |
| Find Middle | Fast moves 2x, slow moves 1x — slow ends at middle | Middle of the Linked List (LC 876) |
| Merge Two Sorted | Dummy head + compare-and-pick smaller node | Merge Two Sorted Lists (LC 21) |
| Merge K Sorted | Min-heap of k list heads, or divide-and-conquer merge | Merge k Sorted Lists (LC 23) |
| Remove Nth from End | Two pointers with n-step gap | Remove Nth Node From End (LC 19) |
| Reorder / Rearrange | Split at middle + reverse second half + interleave merge | Reorder List (LC 143) |
| Sort a Linked List | Merge sort — find middle, split, sort halves, merge | Sort List (LC 148) |
| Intersection Point | Two-pass: when one reaches end, redirect to other's head | Intersection of Two Linked Lists (LC 160) |
Deep Dive: Reorder List (Combines 3 Templates)
Reorder List is the ultimate linked list problem because it combines three core techniques into one solution. Given 1→2→3→4→5, produce 1→5→2→4→3.
Problems mentioned above
function reorderList(head: ListNode | null): void { if (!head?.next) return; // Step 1: Find middle (fast & slow) let slow = head, fast = head; while (fast.next?.next) { slow = slow.next!; fast = fast.next.next; } // Step 2: Reverse second half (3-pointer reversal) let prev: ListNode | null = null; let curr: ListNode | null = slow.next; slow.next = null; // cut the list in half while (curr) { const next = curr.next; curr.next = prev; prev = curr; curr = next; } // Step 3: Merge two halves alternately (interleave) let first: ListNode | null = head; let second: ListNode | null = prev; while (second) { const tmp1 = first!.next; const tmp2 = second.next; first!.next = second; second.next = tmp1; first = tmp1; second = tmp2; } } // 1→2→3→4→5 → split: [1→2→3] + [5→4] → merge: 1→5→2→4→3
Deep Dive: Sort List (Merge Sort on Linked List)
Sorting a linked list is a classic interview question. Merge sort is ideal because linked lists support O(1) splitting (unlike arrays). The approach: find middle → split → recursively sort both halves → merge.
function sortList(head: ListNode | null): ListNode | null { if (!head?.next) return head; // base case: 0 or 1 node // Find middle and split let slow = head, fast = head.next; while (fast?.next) { slow = slow.next!; fast = fast.next.next; } const mid = slow.next; slow.next = null; // cut // Recursively sort both halves const left = sortList(head); const right = sortList(mid); // Merge sorted halves (Template 3) return mergeTwoLists(left, right); } // Time: O(n log n) | Space: O(log n) for recursion stack
Visual Walkthrough
Let's trace through two fundamental operations step by step to build muscle memory for pointer manipulation.
Reversing a Linked List — Step by Step
Input: 1 → 2 → 3 → 4 → null Initial state: prev = null curr = 1 → 2 → 3 → 4 → null Step 1: next = 2, reverse: 1 → null, advance prev = 1 → null curr = 2 → 3 → 4 → null Step 2: next = 3, reverse: 2 → 1, advance prev = 2 → 1 → null curr = 3 → 4 → null Step 3: next = 4, reverse: 3 → 2, advance prev = 3 → 2 → 1 → null curr = 4 → null Step 4: next = null, reverse: 4 → 3, advance prev = 4 → 3 → 2 → 1 → null curr = null ← loop ends Return prev: 4 → 3 → 2 → 1 → null ✓ Key: At each step, we only change ONE pointer (curr.next = prev). The "next" variable saves our place so we don't lose the rest of the list.
Floyd's Cycle Detection — Why It Works
Input: 1 → 2 → 3 → 4 → 5 → 3 (cycle back to node 3) Step 1: slow=1, fast=1 Step 2: slow=2, fast=3 Step 3: slow=3, fast=5 Step 4: slow=4, fast=4 ← THEY MET! Cycle detected. Why does this work? - If there's no cycle, fast reaches null → we return false. - If there IS a cycle, fast enters the cycle first. - Once both are in the cycle, fast gains 1 step per iteration. - The gap closes by 1 each step → they MUST meet. Finding the cycle START (phase 2): - After meeting, reset slow to head. - Move both at speed 1. slow=1, fast=4 slow=2, fast=5 slow=3, fast=3 ← THEY MET at node 3 = cycle start! Why? Mathematical proof: the distance from head to cycle start equals the distance from meeting point to cycle start (mod cycle length).
Intersection of Two Lists — The Elegant Two-Pass Trick
List A: 1 → 2 → 3 ↘ 7 → 8 → null List B: 4 → 5 → 6 ↗ Length A path: 1→2→3→7→8 = 5 nodes Length B path: 4→5→6→7→8 = 5 nodes Trick: When pointer A reaches end, redirect to head of B. When pointer B reaches end, redirect to head of A. pA: 1 → 2 → 3 → 7 → 8 → [switch to B] → 4 → 5 → 6 → 7 ✓ pB: 4 → 5 → 6 → 7 → 8 → [switch to A] → 1 → 2 → 3 → 7 ✓ Both travel exactly (lenA + lenB) steps before meeting at node 7! Why? After switching, both pointers have "compensated" for the length difference. They're now the same distance from the intersection. If no intersection, both reach null at the same time.
Practice Problems
These 7 problems are carefully ordered from easy to hard. Each one teaches a different core linked list technique. Solve them in order.
LC 206. Reverse Linked List
Why this pattern:
The foundational linked list problem. Tests your ability to manipulate pointers with the 3-pointer reversal technique. Every other linked list problem builds on this.
Key idea: Use prev, curr, next. At each step: save next, reverse curr.next to prev, advance both pointers. Return prev when curr is null. Practice until you can write this in your sleep.
LC 21. Merge Two Sorted Lists
Why this pattern:
Dummy head + comparison walk. Tests your ability to build a new list by choosing between two candidates at each step. This is the merge step of merge sort.
Key idea: Create a dummy node. Compare l1.val and l2.val, attach the smaller one to tail, advance that pointer. When one list is exhausted, attach the remainder of the other.
LC 141. Linked List Cycle
Why this pattern:
Fast & slow pointers (Floyd's algorithm). If there's a cycle, the fast pointer will eventually catch up to the slow pointer. If no cycle, fast reaches null.
Key idea: slow moves 1 step, fast moves 2 steps. If they ever point to the same node, there's a cycle. If fast or fast.next is null, no cycle. The key insight: in a cycle, the gap between fast and slow decreases by 1 each step.
LC 19. Remove Nth Node From End of List
Why this pattern:
Two-pointer gap technique. The first pointer gets a head start of n steps, then both move together. When the first reaches the end, the second is at the right spot.
Key idea: Use a dummy node (handles edge case of removing the head). Advance first pointer n+1 steps ahead of second. Move both until first is null. Now second.next is the node to remove — skip it with second.next = second.next.next.
LC 234. Palindrome Linked List
Why this pattern:
Combines three techniques: (1) find middle with fast/slow, (2) reverse the second half, (3) compare both halves node by node. This is the 'combo problem' that tests all your fundamentals.
Key idea: Find middle → reverse from middle to end → compare first half with reversed second half. If all values match, it's a palindrome. Optionally restore the list by reversing the second half again.
LC 143. Reorder List
Why this pattern:
Another combo problem: find middle + reverse second half + interleave merge. Given 1→2→3→4→5, produce 1→5→2→4→3. Tests your ability to chain multiple linked list operations.
Key idea: Split at middle, reverse the second half, then merge the two halves by alternating nodes. The interleave step requires careful pointer management — save next pointers before rewiring.
LC 25. Reverse Nodes in k-Group
Why this pattern:
Extension of basic reversal — reverse k nodes at a time, then connect the reversed groups. Requires counting ahead to check if k nodes remain, then performing a bounded reversal.
Key idea: For each group: (1) check if k nodes remain, (2) reverse exactly k nodes using the standard 3-pointer technique, (3) connect the previous group's tail to the new group's head. Use a dummy node to handle the first group cleanly.
Practice strategy
For each problem: (1) Draw the linked list on paper. (2) Trace the pointer changes step by step. (3) Identify which template(s) apply. (4) Code it. (5) After solving, write: "This uses [template] because [signal]."
Common Mistakes
These are the traps that catch people on linked list problems. Learn them here so you don't learn them in an interview.
Losing the reference to the next node during reversal
You write curr.next = prev but forget to save curr.next first. Now you've lost the rest of the list and can't continue traversing.
✅ALWAYS save the next node before modifying any pointer: const next = curr.next; THEN curr.next = prev. This is the #1 linked list bug.
Infinite loop from incorrect pointer rewiring
You create a cycle accidentally. For example, in reversal you forget to set the old head's next to null, creating a loop between the first two nodes.
✅After any pointer manipulation, mentally trace the list from head to end. Ask: 'Does every path eventually reach null?' If not, you have a cycle. Draw it on paper.
Not using a dummy head node
You write special-case code for 'what if the head needs to be removed?' or 'what if the list is empty?' This leads to messy, bug-prone code.
✅Start with const dummy = new ListNode(0, head) and return dummy.next at the end. This handles all edge cases (empty list, single node, head removal) automatically.
Off-by-one in fast/slow pointer initialization
For finding the middle of an even-length list, whether slow lands on the first or second middle node depends on your loop condition. Getting this wrong breaks palindrome and reorder problems.
✅For 'first middle' (useful for splitting): while (fast?.next?.next). For 'second middle': while (fast?.next). Test with both even and odd length lists.
Forgetting to cut the list when splitting
You find the middle but forget to set middle.next = null. Now both 'halves' still share the second half, causing incorrect results in merge sort or palindrome check.
✅After finding the split point, ALWAYS sever the connection: const secondHalf = mid.next; mid.next = null; Now you have two independent lists.
Using null checks inconsistently
You access node.next.next without checking if node.next is null first. This causes runtime errors on short lists or at the end of traversal.
✅Use optional chaining (fast?.next?.next) or explicit checks. Always test your solution with: empty list, single node, two nodes, and odd/even length lists.
Interview Strategy
Linked list problems are popular in interviews because they test pointer manipulation, edge case handling, and the ability to think visually. Here's how to approach them.
The 5-Step Interview Flow
Clarify the Structure
'Is this a singly or doubly linked list? Can there be cycles? Is the list sorted? Should I modify the list in-place or return a new one?' — These questions determine which template to use.
Draw It Out
Sketch the linked list on the whiteboard. Draw the nodes as boxes with arrows. This is NOT optional for linked list problems — visual reasoning prevents 90% of pointer bugs.
Identify the Template(s)
'This is a reversal problem' or 'I need to find the middle first, then reverse the second half' — name the technique explicitly. For combo problems, list the steps: 'Step 1: find middle. Step 2: reverse. Step 3: merge.'
Code with a Dummy Head
Start with a dummy node unless you're sure you don't need one. Use descriptive names: prev, curr, next for reversal. slow, fast for cycle/middle. l1, l2, tail for merge.
Test Edge Cases Explicitly
Trace through: (1) empty list (null), (2) single node, (3) two nodes, (4) odd vs even length. Say these out loud: 'Let me check the empty list case — dummy.next is null, so we return null. Correct.'
Common Follow-Up Questions
| Follow-Up | How to Handle |
|---|---|
| "Can you do it in O(1) space?" | This usually means: don't copy to an array. Use pointer manipulation (reversal, fast/slow) instead. Linked lists are designed for in-place operations. |
| "What if it's a doubly linked list?" | Reversal becomes easier (swap prev and next for each node). Deletion is O(1) if you have the node reference. Mention that doubly linked lists use more memory. |
| "Can you do it without modifying the original list?" | For palindrome: use a stack to store the first half, then compare with the second half. For reversal: create a new reversed copy. Mention the space trade-off. |
| "What if the list has millions of nodes?" | Emphasize O(1) space solutions. Avoid recursion (stack overflow risk). Mention that linked lists have poor cache locality — in practice, arrays are faster for large n. |
| "Extend to k sorted lists?" | Use a min-heap of size k to always pick the smallest head. Or use divide-and-conquer: merge pairs of lists, halving the count each round. Both are O(N log k). |
| "What about thread safety?" | Linked list operations aren't atomic. Concurrent modifications need locks or lock-free techniques (CAS). Mention ConcurrentLinkedQueue in Java as a real-world example. |
The meta-skill
Linked list problems test your ability to think about pointers and references — a fundamental CS concept. Interviewers watch for: (1) Do you draw diagrams? (2) Do you handle edge cases? (3) Can you combine techniques for complex problems? Practice drawing pointer diagrams until it's second nature.
Summary & Cheat Sheet
Pin this section. Review it before mock interviews and contests.
Quick Revision Cheat Sheet
Core idea: Linked list problems are pointer manipulation problems — rewire .next pointers, don't copy data
Dummy head: Use const dummy = new ListNode(0, head) to eliminate edge cases. Return dummy.next
Reversal: 3 pointers: prev, curr, next. Save → reverse → advance. O(n) time, O(1) space
Fast & slow: slow moves 1x, fast moves 2x. Middle: fast?.next?.next. Cycle: if they meet
Cycle entry: After detection, reset one to head, both move at speed 1. They meet at cycle start
Merge sorted: Dummy head + compare-and-pick. Attach remainder when one list is exhausted
Nth from end: First pointer gets n-step head start. Both move together. First hits end → second is at target
Combo problems: Palindrome / Reorder = find middle + reverse second half + compare/merge
Sort linked list: Merge sort: find middle → split → sort halves → merge. O(n log n)
Intersection: Two-pass trick: redirect to other's head at end. Both travel lenA + lenB steps
Edge cases: Always test: null, single node, two nodes, odd/even length, cycle
Mental trigger: "ListNode" + "in-place" + "O(1) space" → pointer manipulation. Draw it first.
Decision Flowchart
What does the problem ask? ├── Reverse the list (or part of it)? │ ├── Entire list → 3-pointer iterative reversal │ ├── Between positions m and n → Bounded reversal with boundary tracking │ └── In groups of k → Count k ahead, reverse group, chain groups ├── Find something positional? │ ├── Middle node → Fast & slow pointers │ ├── Nth from end → Two-pointer gap technique │ └── Intersection point → Two-pass redirect trick ├── Detect or handle a cycle? │ ├── Does cycle exist? → Fast & slow, check if they meet │ └── Where does cycle start? → Floyd's: meet → reset → walk at same speed ├── Merge or sort? │ ├── Merge 2 sorted lists → Dummy head + compare walk │ ├── Merge k sorted lists → Min-heap or divide-and-conquer │ └── Sort a linked list → Merge sort (find middle + split + merge) └── Rearrange nodes? ├── Palindrome check → Find middle + reverse half + compare ├── Reorder list → Find middle + reverse half + interleave └── Partition around value → Two dummy lists + reconnect
One last thing
Linked list problems reward practice more than any other topic. The techniques are few and well-defined — reversal, fast/slow, merge, dummy head. But the pointer manipulation requires muscle memory. Solve the 7 practice problems in Section 07 until you can write each solution without hesitation. Draw the pointers. Trace the steps. That's how you build the intuition.