DAY 16 / 30 · WEEK 3

Linked Lists II — Fast/Slow & Reverse

Goal of the day

Concept

Think of a group of people standing in a line holding hands, each one told "point your free hand at whoever is in front of you." To reverse the line, everyone has to let go of the person in front and instead grab the person behind them — one at a time, because if two people let go and re-grab in the wrong order, the chain splits and part of the line is lost forever. That's exactly Day 15's "capture before you overwrite" lesson, applied to every node instead of just one.

Reversal. Walk the list once, and at each node, flip its .next to point backward instead of forward. Three pointers make this safe: prev (the reversed portion built so far), cur (the node being flipped right now), and a temporary next (saved before cur.next is overwritten, or the rest of the list is lost — same trap as Day 15's delete).

Fast/slow pointers. Start two pointers at head. Move slow one step at a time, fast two steps at a time. When fast reaches the end, slow has covered exactly half the distance — it's at the middle. The same setup detects a cycle: if the list loops back on itself, fast never hits a real end. Instead, because fast gains exactly one node on slow every step once both are inside the loop, it's guaranteed to catch up and land on the exact same node as slow within one full trip around the cycle — it can't "jump over" slow, since a gain of one node per step can't skip a node entirely.

Diagram: reversing the pointers

Mid-reversal: prev=2, cur=3 — 2 iterations done 1 2 3 4 5 NULL prev cur still forward Reversed portion (green): 2 → 1 → NULL. Untouched portion (gray): 3 → 4 → 5, still pointing the original direction. Next: save cur.next (4) BEFORE flipping cur.next to point at prev (2) — the exact same "capture before overwrite" rule from Day 15's delete.

Diagram: fast/slow finds the middle

List [1, 2, 3, 4, 5] — fast moves 2x speed 1 2 3 4 5 slow (2 steps) fast (4 steps, at end) fast.next is null — the loop stops here. slow has moved exactly half as far as fast, so slow's node (3) is the middle. One pass, no counting the length first and no second walk.

Interactive visualization: reverse a linked list

reversed portion cur (being flipped now)
Press Play or Step to begin.

Interactive visualization: fast/slow pointers find the middle

slow (1 step) fast (2 steps) middle found
Press Play or Step to begin.

The code: reverse, and find the middle

function reverseList(head) {
  let prev = null, cur = head;
  while (cur) {
    const next = cur.next;   // ① save it — we're about to overwrite cur.next
    cur.next = prev;         // ② flip the pointer backward
    prev = cur;               // advance prev to where cur was
    cur = next;                // advance cur to the node we saved in step ①
  }
  return prev; // prev is the new head once cur runs off the end (becomes null)
}

function middleNode(head) {
  let slow = head, fast = head;
  while (fast && fast.next) {   // stop if fast can't take a full 2-step move
    slow = slow.next;
    fast = fast.next.next;
  }
  return slow;
}

Line by line:

Dry run: reverseList on [1, 2, 3, 4, 5]

Iterationnext (saved)cur.next = prevprevcur
121 → NULL12
232 → 123
343 → 234
454 → 345
5null5 → 45null

cur becomes null after iteration 5 — loop ends, return prev (node 5). Final list: 5 → 4 → 3 → 2 → 1 → NULL.

Complexity

OperationTimeSpaceWhy
Reverse (iterative)O(n)O(1)One pass, three pointer variables — no extra list, no call stack growth.
Reverse (recursive)O(n)O(n)Same number of steps, but each recursive call adds a stack frame (Day 9) — n frames deep at the bottom of the recursion.
Find middle (fast/slow)O(n)O(1)One pass — no need to count the length first and walk again.
Cycle detection (Floyd's)O(n)O(1)Same two pointers; no hash set of visited nodes needed, unlike the O(n)-space alternative.

Interview corner

How to talk through it out loud: for reversal, narrate the pointer dance before touching code — "save next, flip cur's pointer backward, slide prev and cur both forward by one." For fast/slow, state the invariant — "fast always covers exactly twice the distance slow does, so when fast runs out of room, slow is exactly at the midpoint." Naming these invariants out loud is what separates "I memorized this pattern" from "I can rebuild it from scratch if I blank on the exact code."

Practice problems

1. Reverse Linked List (Easy — LeetCode: Reverse Linked List)

Reverse a singly linked list and return the new head — today's core technique, applied directly.

2. Linked List Cycle (Easy — LeetCode: Linked List Cycle)

Given the head of a linked list, determine if it contains a cycle, using O(1) extra space.

3. Reorder List (Medium — LeetCode: Reorder List)

Given 1 → 2 → 3 → 4, reorder it in place to 1 → 4 → 2 → 3 — alternating from the front and back toward the middle. Combines both of today's techniques.

Quiz

1. In the reversal loop, why must next be saved before cur.next = prev runs?

2. What's the advantage of fast/slow pointers over "count the length, then walk halfway"?

3. Why can't fast "jump over" slow without ever meeting it, once both are inside a cycle?

4. What's the space-complexity trade-off between iterative and recursive list reversal?

5. Which two techniques does Reorder List combine?