Day 26 ยท Treasure hunts

Linked Lists

You will be able to
  • Build singly linked lists from node objects and traverse them without losing the head
  • Explain the array-vs-linked-list trade: O(1) index vs O(1) splice
  • Reverse a linked list iteratively with the three-pointer dance
  • Detect a cycle with Floyd's fast/slow pointers and explain why they must meet
  • Sketch how a dict + linked list combine into an LRU cache
Today's ~115 minutes
Spaced-rep warm-up: Days 23โ€“25 cards + due deck10 min
ELI5 + tech read, watch the linked-list visualizer20 min
Guided: build/splice + reversal dance + cycle detector35 min
Practice: merge sorted lists + Floyd vs seen-set20 min
Project: pattern log + LRU sketch20 min
Quiz + flashcards10 min

Builds on: Day 9 โ€” Object-oriented Python โ€” classes & references ยท Day 22 โ€” Big O & complexity ยท Day 24 โ€” Fast/slow pointers

The analogy

A treasure hunt: the first clue is in your hand, and each clue names WHERE THE NEXT ONE IS. Clue 1 โ†’ under the bridge; there you find clue 2 โ†’ inside the oak. You cannot jump to clue 7 โ€” you must walk the chain. That is a linked list: each node holds a value and a pointer to the next node, and all you ever hold is the first clue (the head). Lose the head, lose the whole hunt.

Why organize anything this way, when numbered shelves (Day 23's arrays) let you jump anywhere instantly? Because of what INSERTING costs. Adding a shelf in the middle of a numbered warehouse means renumbering โ€” shifting โ€” everything after it. Adding a clue mid-hunt costs almost nothing: write one new clue, and change ONE existing clue to point to it. Nobody else in the chain even notices. That's the trade in full: arrays are instant to *read anywhere*, expensive to *splice*; linked lists are expensive to read anywhere, instant to splice โ€” once you're standing at the right spot. Today's classic moves โ€” reversing the hunt, detecting a prankster's clue-loop โ€” are really exercises in pointer surgery: relinking "next" arrows without ever dropping the chain.

Why this matters on the job

Honestly: you will rarely hand-build a linked list in production Python. You learn them for two real reasons. First, they are the purest gym for reference reasoning โ€” the "names are pointers" model from Day 9 under load โ€” and interviewers use them precisely because pointer bugs are unfakeable; reverse-a-list and detect-cycle remain screening staples. Second, the structure lives inside things you WILL use: deque (yesterday), the LRU caches behind functools.lru_cache (Day 12) and every serious caching layer (Day 47, Day 156) โ€” dict for O(1) find + linked list for O(1) reorder is a genuinely great design you should be able to sketch.

Watch it happen

The treasure hunt โ€” each node only knows the next clue

step 1 / 6
headโ†’4โ†’7โ†’9โ†’null

A linked list: each node holds a value and a pointer to the next node. No indexes, no shelf โ€” to reach anything you follow clues from head.

Guided practice

guided 1

Build, traverse, splice

15 min
  1. In linked_lab.py, define ListNode and two helpers you will reuse all day: from_list(values) building a chain from a Python list, and to_list(head) doing the reverse. Test that they round-trip.
  2. Write insert_after(node, val) โ€” two assignments โ€” and in a comment explain what breaks if you swap their order (draw the arrows if it helps).
  3. Write delete_after(node) (one assignment: route around the victim). Note the asymmetry: with only "next" pointers, deleting a node you are STANDING ON requires knowing its predecessor โ€” walk from the head or use a dummy.
  4. Use the dummy-head trick to write delete_value(head, val) that works even when the head itself must go. Test deleting the head, a middle node, and a missing value.
๐Ÿ python โ€” editable, runs in your browser
Ctrl/โŒ˜+Enter runs ยท Tab indents ยท numpy/pandas/sklearn auto-load on import (torch and network calls need a local run)
guided 2

The reversal dance + the cycle detector

20 min
  1. Implement reverse(head) from the tech section โ€” but FIRST hand-trace it on paper for the 3-node list [1, 2, 3]: draw all three arrows at each of the three iterations. The paper trace is the exercise; the code is its transcript.
  2. Test: reverse of [1..5], of [1], and of [] (None in, None out).
  3. Implement has_cycle(head) with Floyd's fast/slow. Build a test cycle by hand: make the tail's next point at the second node. (Careful: to_list on a cyclic list runs forever โ€” that is the point.)
  4. In a comment, write the meeting argument in your own words: in a cycle, the gap between fast and slow shrinks by exactly 1 per step, so it reaches 0 โ€” no skipping over.
  5. Also write find_middle(head) in 5 lines: when fast hits the end, slow stands at the middle. Two uses of one pattern in one day.
๐Ÿ python โ€” editable, runs in your browser
Ctrl/โŒ˜+Enter runs ยท Tab indents ยท numpy/pandas/sklearn auto-load on import (torch and network calls need a local run)

On your own

Merge, and the space trade

20 min

(1) Merge two sorted lists into one sorted list by relinking existing nodes (no new nodes, no value copying). Use a dummy head and a tail pointer; state the complexity.

(2) Re-implement has_cycle_set(head) using a Day 23 seen-set of nodes, then write a three-sentence comparison: what does Floyd save, what does the set version win on (simplicity? finding WHERE the cycle starts?), and which would you write under interview pressure?

Hints: for (1) the loop compares the two current heads, links the smaller to the merged tail, and advances that list; when one list runs out, link the survivor whole โ€” do not walk it.

Ship before you stop

Pattern log: pointer surgery + the LRU sketch

Add the "Linked Lists" section to patterns.md: the array-vs-list trade table (index, search, splice-at-known-node, memory locality), your solved problems (delete-with-dummy, reverse, cycle, middle, merge) each with its one-line trick, recognition cues ("reverse in place", "cycle", "middle of", "merge lists", "O(1) removal"), and honest costs (O(n) access, cache-unfriendly memory scatter, Python overhead per node). Then the capstone of the section: a half-page LRU cache design sketch โ€” a labeled drawing (ASCII art is fine) of dict + doubly-linked list, and a table showing get/put/evict all hitting O(1), with one sentence each on which structure provides it. Commit.

Rubric โ€” check what you completed (0/5)

Common mistakes & misconceptions

  • Losing the head: advancing your only reference during traversal, then having nothing to return. Keep a separate cursor name (node = head), or use a dummy whose .next you return.
  • Flipping current.next before saving current.next. The three-pointer dance exists because that one assignment destroys your only route forward โ€” save nxt first, always.
  • Comparing nodes with == when you mean identity. Two nodes can hold equal values; cycle detection asks "same NODE?" โ€” use `is`.
  • Forgetting the null guards in fast/slow: fast and fast.next must both be checked before fast.next.next, or odd-length lists crash with AttributeError on None.
  • Special-casing the head in insert/delete instead of using a dummy node. The dummy costs one line and deletes an entire class of off-by-one bugs.
  • Concluding linked lists beat arrays because "insert is O(1)". That O(1) requires already standing at the splice point; finding it is O(n). Arrays with their cache-friendly layout win most real workloads โ€” know the trade, not the slogan.
Knowledge check

Q1. In the iterative reversal loop, why must nxt = current.next happen before current.next = prev?

Q2. Why must fast and slow pointers meet if the list has a cycle?

Q3. An LRU cache needs O(1) get AND O(1) update of usage order. The classic design isโ€ฆ

Go deeper โ€” curated resources

toolVisuAlgo โ€” linked list operations, animated โ†—15 minbookOpenDSA โ€” linked lists chapter โ†—25 mincourseNeetCode Roadmap โ€” Linked List section โ†—20 min
If you have a third hour
  • Reverse a linked list recursively โ€” A perfect bridge to tomorrow: the recursive reversal is 5 lines, and understanding where the "work" happens (after the recursive call returns) previews the call-stack thinking Day 27 makes explicit.
Done means
  • All lab asserts pass, including the manufactured cycle
  • Reversal hand-trace on paper completed before coding
  • Merge relinks nodes without copying; comparison paragraph written
  • patterns.md section + LRU sketch committed; quiz โ‰ฅ 2/3
How this connects

โ† Back: Nodes are Day 9's objects; every .next is Day 9's "names are references" made structural. Fast/slow arrived on Day 24 as an array trick โ€” today it earned its fame. The seen-set alternative is Day 23 again.

Forward โ†’: Day 27's recursion traverses these same chains implicitly via the call stack. Trees (Day 29) are nodes with TWO nexts. The LRU sketch becomes working code when caching gets serious on Day 47 and pays LLM bills on Day 156.

Unlocks: D29 Binary Trees & BSTs