INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED
01
00/07
01 / COVER STEP 06 · LINKED LIST
INVARIANT · STEP 06 · DECK 3 OF 3
SEVEN HARDS, NO NEW IDEAS

The hard linked-list problems are not new techniques — they are deck 2's tricks composed. Merging is the engine of sort-list and merge-k; reversing a sublist is reverse-in-k-group; a dummy and a ring give you rotate; the clone-with-random-pointer has one beautiful O(1) trick; and the LRU cache is a doubly list married to a hash map. Learn how the pieces fit and the “hard” tag stops meaning difficult and starts meaning assembled.

7Problems
5Patterns
7Units
6Lectures
← → ↑ ↓  or  W A S D  navigate SPACE next   DOUBLE-CLICK to advance I index   G goto problem   P predict H hide solutions   T close video   F fullscreen
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
02 / START HERE READ THIS ONE FIRST

HOW THIS DECK WORKS

This is not a list of problems. It is 7 learning units, and every one runs the same four beats. Go in order and the problems stop feeling random.

ASSUMEDA min-heap (priority queue) in unit 02 — push, pop-min, O(log k). Heaps is step 11 and not built yet, so the unit explains what it needs.

01 INTROWhat the concept is, and what to watch for
02 VIDEOThe lecture, full-width in theatre mode
03 DRILLS2–4 questions checking the lecture landed
04 PROBLEMSThe sheet problems that concept unlocks

Moving around

← ↑Back a slide — or A / W → ↓Forward — or D / S / Space 2×clickDouble-click the right side to advance, left to go back. A single click never moves the deck. IThe index: every problem, clickable, with your progress GJump straight to a problem by its number FFullscreen

While you study

HHide solutions — blurs code and steps so you try first PPredict mode: call the next step before the animation plays it TClose the video — Esc works too ☐ ★Mark solved, or star to revisit. Both are saved automatically.

7 PROBLEMS · EVERY ONE LINKS TO LEETCODE TO SOLVE

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
03 / INDEX PRESS I FROM ANYWHERE

INDEX — ALL 7 PROBLEMS

Click any problem to jump straight to it. Solved ones turn green. Press I from anywhere to come back here. The number is the sheet's row, not your position — the deck teaches in the order that builds, so you will meet them slightly out of numerical sequence.

MERGE FAMILY · 03
SUBLIST REVERSAL · 01
RESTRUCTURE (RING) · 01
DEEP CLONE · 01
CACHE DESIGN · 01
SOLVED HAS A LEETCODE LINK
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
04 / SIGNALS WHEN YOU SEE X, REACH FOR Y

WHICH COMPOSITION, AND WHY

Seven hards, and not one new idea — each is deck 2's tricks assembled. The cards below name the composition a statement is asking for.

“MERGE / SORT A LIST” / “k SORTED LISTS”

the two-way merge of sorted lists is the engine; a heap generalises it to k

MERGE (dummy + pick smaller); MIN-HEAP for kO(n log n) sort · O(N log k) for k
“REVERSE IN GROUPS OF k” / “REORDER”

the 3-pointer reversal applied to a window, with boundary bookkeeping

SUBLIST REVERSAL + reconnectO(n) time · O(1) space
“ROTATE” / “MOVE A BLOCK TO THE FRONT/BACK”

close the list into a ring, walk to the new break, cut

RING + CUT (find length, k %= n)O(n) time · O(1) space
“DEEP COPY” WITH AN EXTRA (random) POINTER

interleave each clone beside its original so random is one hop away

INTERLEAVE-CLONE-SPLIT (or a hash map)O(n) time · O(1) space
“O(1) get AND put” / “LRU / LFU CACHE”

a hash map for lookup, a doubly linked list for recency order

MAP + DOUBLY LIST (move-to-front, evict-tail)O(1) per operation
“THE PROBLEM SOUNDS HARD”

it is almost always two easy pieces — a merge, a reversal, a dummy — composed

DECOMPOSE into the deck-2 tricksrecognise, then assemble
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
05 / CONSTRAINTS BEFORE YOU WRITE A LINE

READ THE ANSWER OFF THE CONSTRAINTS

The hards are about picking the right structure, not raw n. A heap turns merge-k from quadratic to log; a map beside a list makes the LRU O(1). The gold rows are the winning choices.

n ≤
BUDGET
WHAT THAT BUYS YOU
merge one list in at a time
O(N·k)
the naive merge-k — correct but quadratic in k
copy nodes into an array
O(n) space
the brute for sort / rotate / copy — pointers avoid it
min-heap of k heads
O(N log k)
merge-k the right way — the heap is the whole idea
merge sort on the list
O(n log n)
split (fast/slow) + merge (unit 1) — no aux array
interleave / ring / map+list
O(1) space
copy-random, rotate, LRU — the O(1) set-pieces

COMPLEXITY COMES FROM THE STRUCTURE, NOT THE SIZE · HEAP · MERGE-SORT · MAP+LIST

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
06 / ROADMAP WHAT YOU ARE ABOUT TO DO

THE WHOLE RUN, 7 UNITS

Seven set-pieces: the merge family (three), a windowed reversal, a ring rotate, the clone-interleave, and the LRU. Each leans on a deck-2 trick — this deck is about assembly.

UNIT 01

Merge Two Sorted Lists

▶ 18:552 DRILLS1 PROBLEM
UNIT 02

Merge k Sorted Lists

▶ 30:022 DRILLS1 PROBLEM
UNIT 03

Sort a List (Merge Sort)

▶ 22:112 DRILLS1 PROBLEM
UNIT 04

Reverse Nodes in k-Group

▶ 24:312 DRILLS1 PROBLEM
UNIT 05

Rotate a List

▶ 12:102 DRILLS1 PROBLEM
UNIT 06

Clone with Random Pointer

▶ 33:002 DRILLS1 PROBLEM
UNIT 07

LRU Cache — List + Map

NO LECTURE2 DRILLS1 PROBLEM
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
07 / WARMUP LOAD THE STRUCTURE CHOICES FIRST · 1 OF 2

THREE THINGS WORTH HAVING IN MEMORY

DRILL 01 · RECALL

Merging k sorted lists by merging them in one at a time is O(N·k). What makes the heap approach O(N log k)?

You only ever choose among k candidates. The next node of the answer is the minimum of the k list-heads, so a min-heap of those heads delivers it in O(log k); pop it, push its successor, repeat. Over all N nodes that is O(N log k). Merging one list in at a time re-scans the growing result, doing O(N·k) work. Same answer, but the heap keeps the comparison set tiny — that is the entire optimisation.

DRILL 02 · RECALL

Why is merge sort the natural choice for sorting a linked list, where quicksort is the usual array default?

Lists give merge sort everything and quicksort nothing. Merge sort splits by walking to the middle (fast/slow) and combines by re-linking nodes — no random access, no extra array, stable, guaranteed O(n log n). Quicksort relies on O(1) indexing to pick and partition around a pivot, which a linked list cannot provide, so it degrades. This is why “sort a linked list” is really “merge sort a linked list.”

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
08 / WARMUP LOAD THE STRUCTURE CHOICES FIRST · 2 OF 2

THREE THINGS WORTH HAVING IN MEMORY

DRILL 01 · RECALL

The LRU cache needs O(1) get and put. Why does it take BOTH a hash map and a doubly linked list — why not one structure?

Neither structure alone is O(1) for both jobs. A hash map finds a node by key instantly but has no notion of recency order; a doubly linked list maintains recency (move a node to the front, drop the tail) in O(1) but cannot find a key without scanning. Together, the map's value is a pointer into the list, so get locates the node via the map and splices it to the front, and put evicts the tail when full — every step O(1). It is the canonical “two structures, complementary weaknesses” design.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
09 / INTRO UNIT 01 · Merge Two Sorted Lists

UNIT 01 — Merge Two Sorted Lists

Merging two sorted lists is the engine under sort-list and merge-k, so it earns its own unit. Keep a dummy head and a tail; at each step compare the two fronts, attach the smaller to tail, and advance that list. When one list empties, the other is already sorted — attach its whole remaining tail in O(1). No new nodes, just re-linking.

THE QUESTION THIS LECTURE ANSWERS

HOW DO YOU MERGE TWO SORTED LISTS INTO ONE, WITHOUT COPYING NODES?

dummy + tailpick the smallerattach leftoverre-linkthe merge engine
WHAT TO WATCH FOR
  • 01A DUMMY HEAD MAKES THE FIRST ATTACH UNIFORM — RETURN dummy.next
  • 02EACH STEP: ATTACH THE SMALLER FRONT, ADVANCE THAT LIST, MOVE tail
  • 03WHEN ONE EMPTIES, ATTACH THE OTHER'S WHOLE TAIL — DO NOT KEEP COMPARING
  • 04IT RE-LINKS EXISTING NODES; O(n+m) TIME, O(1) SPACE
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
10 / VIDEO UNIT 01 · Merge Two Sorted Lists

L23. Merge two sorted Linked Lists

STRIVER A2Z
Merge Two Sorted Lists
RUNTIME 18:55
AFTER THIS → 2 DRILLS · PROBLEM #01
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
11 / DRILL UNIT 01 · Merge Two Sorted Lists

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Once one of the two lists is exhausted, what do you do with the other?

Attach the whole leftover tail at once. The remaining list is already sorted and every one of its values is ≥ the last one you placed, so you simply set tail->next to its head — a single pointer assignment, O(1). Continuing to loop and compare against nothing wastes time and invites null-pointer slips. This “attach the rest” move is why the merge is linear and clean.

DRILL 02 · TRANSFER

Why does this merge routine reappear in both sort-list and merge-k-lists?

It is the reusable combine step. Merge sort splits then merges two sorted halves — this routine. Merge-k either runs log k rounds of pairwise merges (this routine, repeatedly) or uses a heap that is the same “pick the smallest front” idea across k lists. Master the two-way merge and the whole merge family becomes bookkeeping around it.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
12 / MECHANISM UNIT 01 · MERGETWO · CODE MIRRORED

A DUMMY HEAD, THEN PICK THE SMALLER FRONT

Merging two sorted lists is the engine under half of deck 3. Keep a dummy and a tail; at each step compare the two fronts, attach the smaller, and advance that list. When one runs out, the other is already sorted — attach its whole tail in O(1) rather than copying. No new nodes: you are re-linking the existing ones. Return dummy.next.

RE-ROUTING THE ARROWS
CODE MIRROR
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
13 / PROBLEM #01 · MERGE · EASY

Merge Two Sorted Lists

EASY merge ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

Merge two sorted lists into one sorted list.” The base case of the whole merge family — a dummy head and a two-pointer walk.

INTUITION

Dummy head, tail pointer. While both lists have nodes, attach the smaller front to tail and advance that list. When one empties, attach the other's remaining tail whole. Return dummy.next.

STEPS
  1. dummy; tail = &dummy
  2. While both lists are non-empty:
  3. attach the smaller-valued front to tail; advance that list; move tail
  4. Attach the non-empty leftover (tail->next = a ? a : b)
  5. Return dummy.next
BRUTEO((n+m) log) if you collect + sort
OPTIMALO(n + m)
↕ SCROLL
// Dummy head + pick the smaller front; attach the leftover tail whole.
ListNode* mergeTwoLists(ListNode* a, ListNode* b) {
    ListNode dummy(0);
    ListNode* tail = &dummy;
    while (a && b) {
        if (a->val <= b->val) { tail->next = a; a = a->next; }
        else                  { tail->next = b; b = b->next; }
        tail = tail->next;
    }
    tail->next = a ? a : b;        // one is empty; attach the rest
    return dummy.next;
}
TIMEO(n + m)each node is visited once
SPACEO(1)re-links existing nodes; a dummy only
TRAP

Looping past the end, or copying nodes. Once either list is empty, attach the other's whole tail in one assignment — continuing to compare risks a null dereference. And you re-link the existing nodes; allocating new ones is wasteful. Use <= (not <) to keep the merge stable.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
SOLUTION #01 · MERGE TWO SORTED LISTS

L23. Merge two sorted Linked Lists

The walkthrough for #01 Merge Two Sorted Lists. Watch it, then go straight back and write it yourself.

SOLUTION WALKTHROUGH
L23. Merge two sorted Linked Lists
RUNTIME 18:55
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
14 / INTRO UNIT 02 · Merge k Sorted Lists

UNIT 02 — Merge k Sorted Lists

The smallest un-placed element is always one of the k current list-heads, so keep those heads in a min-heap: pop the smallest, append it, and push that node's successor. Each of the N nodes enters and leaves the heap once at O(log k), so the whole merge is O(N log k) — the right complexity, versus O(N·k) for merging one list in at a time.

THE QUESTION THIS LECTURE ANSWERS

HOW DO YOU MERGE k SORTED LISTS WITHOUT RE-SCANNING A GROWING RESULT?

min-heapk headsO(N log k)pop-min push-nextvs pairwise D&C
WHAT TO WATCH FOR
  • 01THE NEXT OUTPUT IS THE MIN OF THE k CURRENT HEADS — A MIN-HEAP GIVES IT IN O(log k)
  • 02POP THE MIN, APPEND IT, PUSH ITS node->next (KEEP THE HEAP AT SIZE ≤ k)
  • 03O(N log k) TOTAL — VS O(N·k) FOR MERGING ONE LIST IN AT A TIME
  • 04DIVIDE & CONQUER (log k ROUNDS OF PAIRWISE MERGES) HITS THE SAME BOUND
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
15 / VIDEO UNIT 02 · Merge k Sorted Lists

L25. Merge K Sorted Lists | Multiple Approaches

STRIVER A2Z
Merge k Sorted Lists
RUNTIME 30:02
AFTER THIS → 2 DRILLS · PROBLEM #02
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
16 / DRILL UNIT 02 · Merge k Sorted Lists

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

After popping the minimum head from the heap and appending it, what do you push back?

Push the popped node's next. The heap's invariant is “one entry per list, its current head.” When you remove a list's head as the global minimum, that list's new head is node->next, so you push it (if non-null) to keep the invariant. The heap size stays ≤ k, which is what pins each operation at O(log k) and the total at O(N log k).

DRILL 02 · TRANSFER

Merge-k can also be done by divide-and-conquer. What is that approach, and its complexity?

Pairwise merging over log k rounds. Merge lists 1&2, 3&4, …; that halves the number of lists and touches all N nodes once (O(N)). Repeat, and after log k rounds a single sorted list remains — total O(N log k), the same as the heap. It reuses the two-way merge directly, so if unit 1 is solid this is nearly free. Two routes, one bound: recognising both is the mark of really owning merge-k.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
17 / MECHANISM UNIT 02 · MERGEK · CODE MIRRORED

A MIN-HEAP OF THE k HEADS

The smallest remaining element is always one of the k current heads, so keep them in a min-heap: pop the smallest, append it, and push that node's successor. Each of the N nodes is pushed and popped once at O(log k), giving O(N log k) — far better than merging one list in at a time (O(Nk)). The divide-and-conquer alternative, merging lists pairwise over log k rounds, hits the same bound.

RE-ROUTING THE ARROWS
CODE MIRROR
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
18 / PROBLEM #02 · MERGE · HARD

Merge k Sorted Lists

HARD merge ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Merge k sorted lists into one.” The next output is always the minimum of the k current heads — a min-heap, or divide-and-conquer pairwise merging.

INTUITION

Push each list's head into a min-heap keyed by value. Repeatedly pop the smallest, append it to a dummy-headed result, and push that node's next. The heap stays size ≤ k, so each of N nodes costs O(log k).

STEPS
  1. Push each non-null list head into a min-heap (by value)
  2. dummy; tail = &dummy
  3. While the heap is non-empty:
  4. pop the min node; tail->next = it; tail = it; push it->next if present
  5. Return dummy.next
BRUTEO(N·k) merging one at a time
OPTIMALO(N log k)
↕ SCROLL
// A min-heap of the k heads: pop the smallest, push its successor.
ListNode* mergeKLists(vector<ListNode*>& lists) {
    auto cmp = [](ListNode* a, ListNode* b){ return a->val > b->val; };
    priority_queue<ListNode*, vector<ListNode*>, decltype(cmp)> pq(cmp);
    for (ListNode* l : lists) if (l) pq.push(l);

    ListNode dummy(0), *tail = &dummy;
    while (!pq.empty()) {
        ListNode* node = pq.top(); pq.pop();
        tail->next = node; tail = node;
        if (node->next) pq.push(node->next);
    }
    return dummy.next;
}
TIMEO(N log k)N pops/pushes, each O(log k)
SPACEO(k)the heap holds ≤ k nodes
TRAP

Merging one list in at a time (O(N·k)), or an unstable heap comparator. Feeding lists into an accumulator re-walks the growing result; the heap keeps the comparison set at k. In Python, push a tie-breaker (the list index) so the heap never tries to compare two ListNodes directly. Divide-and-conquer pairwise merging is an equally good O(N log k) alternative.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
SOLUTION #02 · MERGE K SORTED LISTS

L25. Merge K Sorted Lists | Multiple Approaches

The walkthrough for #02 Merge k Sorted Lists. Watch it, then go straight back and write it yourself.

SOLUTION WALKTHROUGH
L25. Merge K Sorted Lists | Multiple Approaches
RUNTIME 30:02
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
19 / INTRO UNIT 03 · Sort a List (Merge Sort)

UNIT 03 — Sort a List (Merge Sort)

Sorting a linked list in O(n log n) is merge sort, and a list suits it perfectly: split at the middle with fast/slow, sort each half recursively, then merge the two with the unit-1 routine. No auxiliary array — the combine step just re-links nodes — and unlike quicksort it needs no random access for a pivot.

THE QUESTION THIS LECTURE ANSWERS

HOW DO YOU SORT A LINKED LIST IN O(n log n) WITHOUT AN ARRAY?

merge sortsplit · sort · mergecut the halfstableno aux array
WHAT TO WATCH FOR
  • 01SPLIT AT THE MIDDLE WITH FAST/SLOW — AND CUT THE FIRST HALF (prev->next = null)
  • 02RECURSE ON EACH HALF, THEN MERGE THE TWO SORTED HALVES (UNIT 01)
  • 03MERGE SORT, NOT QUICKSORT — A LIST HAS NO O(1) RANDOM ACCESS FOR A PIVOT
  • 04STABLE, O(n log n) TIME, O(1) EXTRA (BOTTOM-UP) OR O(log n) STACK (TOP-DOWN)
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
20 / VIDEO UNIT 03 · Sort a List (Merge Sort)

L26. Sort a Linked List | Merge Sort and Brute Force

STRIVER A2Z
Sort a List (Merge Sort)
RUNTIME 22:11
AFTER THIS → 2 DRILLS · PROBLEM #03
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
21 / DRILL UNIT 03 · Sort a List (Merge Sort)

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · BUG

This merge sort recurses forever on a 2-node list. What did the split forget?

ListNode* mid = findMiddle(head);
ListNode* left  = sortList(head);
// mid was found, but head's half was never cut
ListNode* right = sortList(mid);

You must cut the list into two independent halves. findMiddle gives you mid, but unless you sever the link before it (prevOfMid->next = nullptr), the “left” half still points straight through mid into the rest, so sortList(head) re-sorts everything and the subproblem never gets smaller — infinite recursion. The split is two steps: find the middle, and terminate the first half.

DRILL 02 · RECALL

Why merge sort rather than quicksort for a linked list?

The list's access pattern fits merge sort and starves quicksort. Merge sort only ever walks forward and re-links nodes, so it needs no auxiliary array and stays a clean O(n log n); splitting is a fast/slow walk and merging is unit 1. Quicksort's speed comes from O(1) indexing to choose a good pivot and partition in place — precisely what a linked list cannot do — so it degrades badly. “Sort a list” means merge sort.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
22 / MECHANISM UNIT 03 · SORTLIST · CODE MIRRORED

MERGE SORT: SPLIT, SORT, MERGE

Sorting a linked list in O(n log n) is merge sort, and lists suit it perfectly: split at the middle with fast/slow, sort each half recursively, then merge the two with the unit-1 routine. Quicksort is a poor fit (no random access for a good pivot), and merge sort needs no auxiliary array here — the combine step just re-links nodes. It is the clearest example of the deck's thesis: a hard problem is two easy ones stacked.

RE-ROUTING THE ARROWS
CODE MIRROR
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
23 / PROBLEM #03 · MERGE · MED

Sort List

MED merge ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

Sort a linked list”, ideally O(n log n) and O(1) space. That is merge sort on a list: split, sort, merge.

INTUITION

Find the middle with fast/slow and cut the list into two halves there. Recursively sort each half, then merge them with the unit-1 routine. Top-down is O(log n) stack; bottom-up is truly O(1) space.

STEPS
  1. Base: 0 or 1 node is already sorted
  2. Split: fast/slow to the middle; cut prevOfMid->next = null
  3. left = sortList(head); right = sortList(mid)
  4. Return mergeTwoLists(left, right)
  5. (Bottom-up merges runs of size 1, 2, 4, … for O(1) space)
BRUTEO(n) space (copy to array, sort)
OPTIMALO(n log n)
↕ SCROLL
// Merge sort: split at the middle, sort each half, merge (unit 1).
ListNode* sortList(ListNode* head) {
    if (!head || !head->next) return head;
    ListNode *slow = head, *fast = head->next;   // find & cut the middle
    while (fast && fast->next) { slow = slow->next; fast = fast->next->next; }
    ListNode* mid = slow->next;
    slow->next = nullptr;                         // CUT the first half
    ListNode* left  = sortList(head);
    ListNode* right = sortList(mid);
    return mergeTwoLists(left, right);            // combine
}
TIMEO(n log n)log n levels, O(n) merge work each
SPACEO(1)bottom-up needs no recursion stack
TRAP

Not cutting the first half. After finding mid you must set the node-before-mid's next to null, or the two halves still share nodes and the recursion never shrinks — a stack overflow. Starting fast at head->next makes slow land on the last node of the first half, which is exactly where you cut.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
SOLUTION #03 · SORT LIST

L26. Sort a Linked List | Merge Sort and Brute Force

The walkthrough for #03 Sort List. Watch it, then go straight back and write it yourself.

SOLUTION WALKTHROUGH
L26. Sort a Linked List | Merge Sort and Brute Force
RUNTIME 22:11
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
24 / INTRO UNIT 04 · Reverse Nodes in k-Group

UNIT 04 — Reverse Nodes in k-Group

Reverse-nodes-in-k-group is the unit-2 three-pointer reversal applied to a window of k at a time. Before reversing a block, confirm it has k nodes (walk k ahead); a trailing block shorter than k is left untouched. After reversing, reconnect: the previous block's tail links to this block's new head, and this block's new tail links onward.

THE QUESTION THIS LECTURE ANSWERS

HOW DO YOU REVERSE THE LIST IN FIXED BLOCKS, LEAVING A SHORT TAIL ALONE?

window of kcheck k firstsublist reversalreconnect boundariesshort tail kept
WHAT TO WATCH FOR
  • 01FIRST CHECK THE BLOCK HAS k NODES — WALK k AHEAD BEFORE REVERSING
  • 02REVERSE THE k NODES WITH prev/curr/next (UNIT 02), THEN RECONNECT THE BOUNDARIES
  • 03PREVIOUS BLOCK'S TAIL → THIS BLOCK'S NEW HEAD; NEW TAIL (OLD HEAD) → NEXT BLOCK
  • 04A FINAL BLOCK SHORTER THAN k STAYS IN ORIGINAL ORDER
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
25 / VIDEO UNIT 04 · Reverse Nodes in k-Group

L21. Reverse Nodes in K Group Size of LinkedList

STRIVER A2Z
Reverse Nodes in k-Group
RUNTIME 24:31
AFTER THIS → 2 DRILLS · PROBLEM #04
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
26 / DRILL UNIT 04 · Reverse Nodes in k-Group

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Before reversing a group of k nodes, what must you verify, and why?

Confirm k nodes remain before touching the block. The problem specifies that a final group smaller than k keeps its original order, so you walk k nodes ahead first; if you fall off the end, return that block unchanged. Reversing an incomplete block (or reversing before checking) is the standard wrong answer. Once the block is known to have k nodes, reverse it with the unit-2 dance and stitch the boundaries.

DRILL 02 · TRANSFER

What is the extra work in reverse-in-k-group beyond a plain reversal?

Boundary bookkeeping between blocks. The reversal itself is unit 2 verbatim; the difficulty is threading the reversed chunks together. After reversing a block, its old head is now the tail and must connect to whatever comes next (the next reversed block, or the leftover), and the previous block must point to this block's new head. Track those two boundary nodes carefully and the “hard” problem is just reversal with plumbing.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
27 / MECHANISM UNIT 04 · KGROUP · CODE MIRRORED

THE 3-POINTER REVERSAL, k NODES AT A TIME

Reverse-in-k-group is the unit-2 reversal applied to a window of k. Before touching a block, check it actually has k nodes (walk k ahead); if not, leave it as-is. Reverse the block with prev/curr/next, then reconnect: the previous block's tail points to this block's new head, and this block's new tail (the old head) links onward. The bookkeeping of the boundary nodes is the only thing beyond a plain reversal.

RE-ROUTING THE ARROWS
CODE MIRROR
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
28 / PROBLEM #04 · SUBLIST-REVERSE · HARD

Reverse Nodes in k-Group

HARD sublist-reverse ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

Reverse the nodes in groups of k”, leaving a trailing block shorter than k as-is. The unit-2 reversal applied to a window, plus boundary reconnection.

INTUITION

Check the next k nodes exist; if not, return the block unchanged. Otherwise reverse those k nodes with prev/curr/next, then recurse on the rest and link the old head to whatever the recursion returns. The old head becomes the block's tail; the last node reversed becomes its head.

STEPS
  1. Walk k nodes ahead; if fewer than k remain, return head unchanged
  2. Reverse the first k nodes (prev/curr/next), stopping at the (k+1)-th
  3. head (now the block's tail) -> reverseKGroup(rest, k)
  4. Return prev — the new head of this block
  5. (Iterative version threads a groupPrev pointer across blocks)
BRUTEO(n) with a stack, O(n) space
OPTIMALO(n)
↕ SCROLL
// Verify k nodes, reverse the block, recurse, reconnect boundaries.
ListNode* reverseKGroup(ListNode* head, int k) {
    ListNode* node = head;
    for (int i = 0; i < k; i++) {              // enough nodes for a full block?
        if (!node) return head;                // fewer than k: keep as-is
        node = node->next;
    }
    ListNode* prev = reverseKGroup(node, k);   // 'node' is the (k+1)-th; recurse
    ListNode* curr = head;
    for (int i = 0; i < k; i++) {              // reverse this block, linking
        ListNode* next = curr->next;           // to the already-reversed rest
        curr->next = prev; prev = curr; curr = next;
    }
    return prev;                               // new head of this block
}
TIMEO(n)each node reversed once
SPACEO(1)O(1) iterative; O(n/k) stack if recursive
TRAP

Reversing before checking the block is full. Walk k nodes ahead first; a trailing group shorter than k must stay in order. The other difficulty is reconnection — each reversed block's old head becomes its tail and must link to the next block's new head, which the recursive form handles by reversing “onto” the already-processed remainder.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
SOLUTION #04 · REVERSE NODES IN K-GROUP

L21. Reverse Nodes in K Group Size of LinkedList

The walkthrough for #04 Reverse Nodes in k-Group. Watch it, then go straight back and write it yourself.

SOLUTION WALKTHROUGH
L21. Reverse Nodes in K Group Size of LinkedList
RUNTIME 24:31
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
29 / INTRO UNIT 05 · Rotate a List

UNIT 05 — Rotate a List

Rotating right by k is pure restructuring: two link changes move a block from the back to the front. Find the length n, take k %= n (rotations wrap), then join the tail to the head to make a ring. The new head sits k nodes before the old tail — at index n − k — so walk to the node just before it and cut the ring.

THE QUESTION THIS LECTURE ANSWERS

HOW DO YOU MOVE THE LAST k NODES TO THE FRONT WITH JUST TWO LINK CHANGES?

k %= nclose the ringnew head at n−kcuttwo link changes
WHAT TO WATCH FOR
  • 01FIND THE LENGTH n AND TAKE k %= n — k CAN EXCEED n, AND MULTIPLES OF n ARE A NO-OP
  • 02CONNECT tail->next = head — NOW IT IS A RING
  • 03THE NEW head IS AT INDEX n − k; THE NEW tail IS THE NODE JUST BEFORE IT
  • 04CUT: newTail->next = null, RETURN newHead
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
30 / VIDEO UNIT 05 · Rotate a List

L22. Rotate a LinkedList

STRIVER A2Z
Rotate a List
RUNTIME 12:10
AFTER THIS → 2 DRILLS · PROBLEM #05
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
31 / DRILL UNIT 05 · Rotate a List

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Why compute k %= n before rotating?

Rotations are cyclic, so only k mod n matters. If k ≥ n, walking k steps overshoots; and rotating by exactly n (or 2n, …) lands back where you started. Taking k %= n collapses all of that to the effective shift, so you walk to index n − k once. Skipping the modulo is the most common rotate bug — it passes small samples and fails when k is large.

DRILL 02 · TRACE

Rotate 1 → 2 → 3 → 4 → 5 right by k = 2. What is the result, and where is the new head?

4 → 5 → 1 → 2 → 3. Rotating right by 2 moves the last two nodes (4, 5) to the front. With n = 5, the new head is at index n − k = 3 (value 4), and the new tail is index 2 (value 3). Close the ring, walk to node 3, cut after it. The MECHANISM slide shows the tail-to-head back-edge forming, then the cut at the new spot.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
32 / MECHANISM UNIT 05 · ROTATE · CODE MIRRORED

CLOSE INTO A RING, THEN CUT AT THE NEW SPOT

Rotating right by k is pure restructuring. Find the length n, take k %= n (rotations wrap), then join the tail to the head to form a ring. The new head is k nodes before the old tail — i.e. at index n − k — so walk to the node just before it (the new tail) and cut the ring there. Two link changes move an arbitrary block from the back to the front.

RE-ROUTING THE ARROWS
CODE MIRROR
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
33 / PROBLEM #05 · RESTRUCTURE · MED

Rotate List

MED restructure ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

Rotate the list right by k.” Restructuring: find the length, close into a ring, and cut at the new head position.

INTUITION

Find the length n and the tail. Take k %= n. Join tail->next = head to form a ring. Walk n − k − 1 steps from the head to the new tail; the node after it is the new head. Cut newTail->next = null.

STEPS
  1. Find length n and the tail node (guard empty / single)
  2. k %= n; if k == 0, return head
  3. tail->next = head (close the ring)
  4. Walk n − k − 1 from head to newTail; newHead = newTail->next
  5. newTail->next = null; return newHead
BRUTEO(n·k) rotating one step at a time
OPTIMALO(n)
↕ SCROLL
// Close into a ring, walk to index n-k-1, cut there.
ListNode* rotateRight(ListNode* head, int k) {
    if (!head || !head->next || k == 0) return head;
    int n = 1; ListNode* tail = head;
    while (tail->next) { tail = tail->next; n++; }   // length + tail
    k %= n;
    if (k == 0) return head;
    tail->next = head;                               // close the ring
    ListNode* newTail = head;
    for (int i = 0; i < n - k - 1; i++) newTail = newTail->next;
    ListNode* newHead = newTail->next;
    newTail->next = nullptr;                         // cut
    return newHead;
}
TIMEO(n)one pass for length, one to the cut point
SPACEO(1)a couple of pointers
TRAP

Forgetting k %= n. k can exceed the length, and rotating by a multiple of n is a no-op; without the modulo you walk off the ring or do needless work. Guard the empty and single-node lists, and remember the new head is at index n − k — the new tail is the node right before it.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
SOLUTION #05 · ROTATE LIST

L22. Rotate a LinkedList

The walkthrough for #05 Rotate List. Watch it, then go straight back and write it yourself.

SOLUTION WALKTHROUGH
L22. Rotate a LinkedList
RUNTIME 12:10
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
34 / INTRO UNIT 06 · Clone with Random Pointer

UNIT 06 — Clone with Random Pointer

Deep-copying a list whose nodes carry an extra random pointer, in O(1) space, hinges on one trick: interleave. Insert each clone right after its original (A → A′ → B → B′ → …). Now the clone of any node X is exactly X->next, so clone->random = X->random->next — no hash map. Finally unweave the two lists apart.

THE QUESTION THIS LECTURE ANSWERS

HOW DO YOU COPY random POINTERS WITHOUT A MAP FROM ORIGINAL TO CLONE?

interleaveclone = X->nextrandom in O(1)unweaverestore original
WHAT TO WATCH FOR
  • 01STEP 1 — WEAVE: INSERT EACH CLONE RIGHT AFTER ITS ORIGINAL
  • 02STEP 2 — RANDOMS: clone->random = orig->random->next (THE CLONE IS ONE HOP ON)
  • 03STEP 3 — UNWEAVE: SPLIT INTERLEAVED LIST INTO ORIGINAL AND CLONE, RESTORING next
  • 04THE HASH-MAP VERSION IS O(n) SPACE; THIS IS THE O(1) FOLLOW-UP
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
35 / VIDEO UNIT 06 · Clone with Random Pointer

L27. Clone a LinkedList with Next and Random Pointers | Copy List with Random Pointers

STRIVER A2Z
Clone with Random Pointer
RUNTIME 33:00
AFTER THIS → 2 DRILLS · PROBLEM #06
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
36 / DRILL UNIT 06 · Clone with Random Pointer

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

After interleaving clones (A → A′ → B → B′ → …), why is a clone's random pointer simply orig->random->next?

Interleaving makes the original→clone map positional. If orig's random target is some node R, then R's clone is sitting right after R — at R->next, i.e. orig->random->next. So the clone's random pointer is available in O(1) with no dictionary. This is the whole reason to weave the clones in beside the originals rather than building a separate list.

DRILL 02 · RECALL

The simpler solution uses a hash map from each original node to its clone. What is the trade-off versus interleaving?

Space versus simplicity. The hash-map approach is easy to reason about — map original→clone, then wire up next and random by lookup — but it costs O(n) extra memory. Interleaving gets to O(1) space by encoding that mapping in the list's own structure, paying with the care needed to weave clones in, set randoms, and cleanly unweave (restoring the original's next pointers). Both are O(n) time; the follow-up that matters asks for O(1) space.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
37 / MECHANISM UNIT 06 · COPYRANDOM · CODE MIRRORED

WEAVE EACH CLONE BESIDE ITS ORIGINAL

Deep-copying a list with random pointers in O(1) space hinges on one idea: interleave. First insert each clone right after its original (A → A′ → B → B′ → …). Now the clone of any node X sits at X->next, so clone->random = X->random->next — no hash map needed. Finally unweave the interleaved list back into the original and the copy. Three linear passes, constant extra space.

RE-ROUTING THE ARROWS
CODE MIRROR
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
38 / PROBLEM #06 · CLONE · MED

Copy List with Random Pointer

MED clone ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Deep copy a list where each node has a random pointer”, ideally O(1) space. The elegant trick is to interleave each clone beside its original.

INTUITION

Weave a clone after each original (A → A′ → B → B′ → …). Then each clone's random is orig->random->next, because the clone of any node sits right after it. Finally unweave the interleaved list back into the original and the copy, restoring the originals' next pointers.

STEPS
  1. Pass 1: insert clone c after each original p (c->next = p->next; p->next = c)
  2. Pass 2: for each original p, p->next->random = p->random ? p->random->next : null
  3. Pass 3: unweave — separate original and clone chains, restoring p->next
  4. Return the clone's head
BRUTEO(n) space (hash map orig→clone)
OPTIMALO(n)
↕ SCROLL
// Interleave clones so clone->random = orig->random->next; then unweave.
Node* copyRandomList(Node* head) {
    if (!head) return nullptr;
    for (Node* p = head; p; p = p->next->next) {          // 1) weave clones in
        Node* c = new Node(p->val);
        c->next = p->next; p->next = c;
    }
    for (Node* p = head; p; p = p->next->next)            // 2) set clone randoms
        p->next->random = p->random ? p->random->next : nullptr;
    Node dummy(0), *ct = &dummy;                          // 3) unweave
    for (Node* p = head; p; p = p->next) {
        ct->next = p->next; ct = ct->next;
        p->next = p->next->next;                          // restore original
    }
    return dummy.next;
}
TIMEO(n)three linear passes
SPACEO(1)no map; clones live inline
TRAP

Setting randoms after splitting, or leaving the original corrupted. Wire the clones' random pointers while still interleaved — once unwoven, orig->random->next no longer points at the clone. And as you split, restore each original's next, or you return a mangled input list.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
SOLUTION #06 · COPY LIST WITH RANDOM POINTER

L27. Clone a LinkedList with Next and Random Pointers | Copy List with Random Pointers

The walkthrough for #06 Copy List with Random Pointer. Watch it, then go straight back and write it yourself.

SOLUTION WALKTHROUGH
L27. Clone a LinkedList with Next and Random Pointers | Copy List with Random Pointers
RUNTIME 33:00
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
39 / INTRO UNIT 07 · LRU Cache — List + Map

UNIT 07 — LRU Cache — List + Map

An LRU cache is two structures married so each covers the other's weakness: a hash map for O(1) lookup of a key's node, and a doubly linked list for O(1) recency ordering — move a used node to the front, evict from the tail (always the least-recently-used). get splices the node to the front; put evicts the tail when full, then inserts at the front.

THE QUESTION THIS LECTURE ANSWERS

HOW DO YOU GET O(1) get AND put, WITH EVICTION OF THE LEAST-RECENTLY-USED?

map + doubly listmove-to-frontevict tailO(1) per oprecency order
WHAT TO WATCH FOR
  • 01HASH MAP: key → node, FOR O(1) LOOKUP; DOUBLY LIST: RECENCY ORDER, head = NEWEST
  • 02get(key): FIND VIA MAP, MOVE THE NODE TO head, RETURN ITS VALUE
  • 03put(key): IF FULL, EVICT THE tail (LRU) AND ERASE ITS MAP ENTRY; INSERT AT head
  • 04UPDATE THE MAP ON EVERY INSERT, MOVE, AND EVICT — STALE ENTRIES ARE THE BUG
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
40 / DRILL UNIT 07 · LRU Cache — List + Map

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

On a get(key) that hits, what are the two actions?

Return the value and promote the node to the front. A get counts as a use, so the node becomes the most-recently-used and must move to the head — otherwise the recency order is wrong and you would eventually evict a hot key. The map locates the node in O(1); the doubly list lets you unlink and re-insert at the front in O(1). Forgetting the move-to-front is a silent correctness bug that only shows up under the right eviction sequence.

DRILL 02 · RECALL

When put must evict because the cache is full, which node goes, and what must not be forgotten?

Evict the tail, and clean the map. By construction the tail is the least-recently-used node, so it is the one to drop. The subtlety is that the hash map still holds that key pointing at the now-removed node; failing to erase it leaves a dangling entry that will later return a freed node or mis-count the size. The invariant is simple but strict: the map and the list must agree after every operation.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
41 / MECHANISM UNIT 07 · LRU · CODE MIRRORED

A HASH MAP FINDS; A DOUBLY LIST ORDERS BY RECENCY

The LRU cache is the marriage of two structures, each covering the other's weakness. A hash map gives O(1) lookup of a key's node; a doubly linked list keeps nodes in recency order with O(1) move-to-front and evict-from-tail (the tail is always the least-recently-used). get splices the node to the front; put evicts the tail when full, then inserts at the front. Every operation is O(1) — and it is exactly why deck 1 taught the doubly list.

RE-ROUTING THE ARROWS
CODE MIRROR
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
42 / PROBLEM #07 · DESIGN · MED

LRU Cache

MED design ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Design an LRU cache with O(1) get and put.” The canonical hash-map-plus-doubly-linked-list design.

INTUITION

A hash map maps key → node for O(1) lookup; a doubly linked list keeps nodes in recency order (head = most recent, tail = least). get finds the node via the map and moves it to the front; put updates or inserts at the front and, if over capacity, evicts the tail and erases its key.

STEPS
  1. Store capacity, a doubly linked list (head=MRU), and a map key→node
  2. get(key): if absent return -1; else move the node to the front, return its value
  3. put(key,val): if present, update and move to front
  4. else insert at front; if size > capacity, evict the tail and erase its key from the map
  5. Every operation is O(1)
BRUTEO(n) per op with a single array/list
OPTIMALO(1) per op
↕ SCROLL
// Hash map (find) + doubly linked list (recency: head=MRU, tail=LRU).
class LRUCache {
    int cap;
    list<pair<int,int>> dll;                       // {key, val}, front = MRU
    unordered_map<int, list<pair<int,int>>::iterator> mp;
public:
    LRUCache(int capacity) : cap(capacity) {}

    int get(int key) {
        auto it = mp.find(key);
        if (it == mp.end()) return -1;
        dll.splice(dll.begin(), dll, it->second); // move node to front
        return it->second->second;
    }
    void put(int key, int value) {
        auto it = mp.find(key);
        if (it != mp.end()) dll.erase(it->second);
        dll.push_front({key, value});
        mp[key] = dll.begin();
        if ((int)dll.size() > cap) {               // evict the LRU tail
            mp.erase(dll.back().first);
            dll.pop_back();
        }
    }
};
TIMEO(1) per opmap lookup + list splice are both O(1)
SPACEO(capacity)one node and one map entry per cached key
TRAP

Stale map entries and the move-to-front. On eviction, erase the tail's key from the map — a dangling entry is the classic LRU bug. And a successful get must promote the node to the front, or recency order rots and a hot key gets evicted. The map and the list must agree after every single operation.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
43 / RECALL RETRIEVAL, NOT RECOGNITION

NAME THE COMPOSITION FROM THE STATEMENT

DRILL 01 · TRANSFER

Sort-list, merge-k, and merge-two look like three problems. What single routine do all three depend on?

The two-way merge. Merge-two-sorted-lists is the primitive; merge sort's combine phase calls it on the two sorted halves; and merge-k is the same idea scaled to k inputs (a min-heap picks the smallest head, or you merge pairwise in log k rounds). Learn the merge once and three-quarters of the “merge” family is written. That is the compositional payoff this deck keeps pointing at.

DRILL 02 · RECALL

Copy-list-with-random can be done with a hash map (original → clone) in O(n) space. What does the interleaving trick buy, and how?

O(1) space instead of O(n). The hash-map solution stores a mapping from every original node to its clone so it can wire the random pointers; the interleaving trick makes that mapping positional — the clone of X is always X->next, so clone->random = X->random->next with no map at all. Three linear passes (weave, set randoms, unweave) and constant extra memory. Recognising that “I need a map from A to B” can sometimes become “put B next to A” is the transferable idea.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
44 / TRAPS PLAUSIBLE, WRONG, AND SILENT

SIX WAYS TO BE PLAUSIBLY WRONG

The hards fail on the seams: an O(N·k) merge, an uncut split, an incomplete k-block, a forgotten modulo, a stale map entry. Every one compiles and passes the sample.

MERGE-k ONE-AT-A-TIME (O(N·k))

Repeatedly merging the next list into an accumulator re-walks the growing result — O(N·k). Use a min-heap of the k heads (O(N log k)) or divide-and-conquer pairwise merges (log k rounds).

SORT LIST: LOSING THE SPLIT

When you split with fast/slow, you must cut the first half by setting the node-before-middle's next to null; forget it and the two halves still share nodes, and the recursion never terminates.

k-GROUP: REVERSING AN INCOMPLETE BLOCK

Only reverse a group once you've confirmed it has k nodes — walk k ahead first. A trailing block shorter than k stays as-is, and reconnecting the reversed block's new tail to the next group is the fiddly part.

ROTATE: k CAN EXCEED n

k may be larger than the length, so take k %= n first — otherwise you walk off the ring. And rotating by a multiple of n is a no-op; the modulo handles that too.

COPY-RANDOM: SPLITTING TOO EARLY, OR NOT RESTORING

Set every clone's random before you unweave — once separated, orig->random->next no longer points at the clone. And restore the original list's next pointers as you split, or you hand back a corrupted input.

LRU: STALE MAP ENTRIES ON EVICT

When you evict the tail node, you must also erase its key from the hash map; leaving a dangling map entry pointing at a freed node is the classic LRU bug. Update the map on every insert, move, and evict.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
45 / CHEATSHEET REVISION SURFACE

ONE SCREEN, THE WHOLE DECK

Seven set-pieces, one page. The right-hand column is the assembly — the structures and moves that turn each “hard” into a couple of easy pieces.

ALGORITHM
TIME
SPACE
REACH FOR IT WHEN
Merge two sorted
O(n+m)
O(1)
dummy + pick the smaller front, attach leftover
Merge k sorted
O(N log k)
O(k)
min-heap of the k heads; or pairwise D&C
Sort list
O(n log n)
O(1)
merge sort: split (fast/slow), sort, merge
Reverse in k-group
O(n)
O(1)
check k nodes, reverse the block, reconnect
Rotate list
O(n)
O(1)
length, k %= n, close ring, cut at n−k
Copy w/ random
O(n)
O(1)
interleave clones, set random, unweave
LRU cache
O(1)/op
O(cap)
hash map + doubly list, move-to-front / evict-tail
INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
46 / CLOSE STEP 06 · DECK 3 OF 3

SEVEN HARDS, NO NEW IDEAS

Merge, reverse-a-window, ring-and-cut, interleave, map-plus-list — every hard was deck 2's tricks assembled. That is the whole of linked lists: a node and a pointer, a handful of moves, and the judgement to see which composition a problem is quietly asking for.

00%
OF THIS DECK SOLVED
← ALL TOPICS← DECK 2 · MEDIUMDECK 1 · FUNDAMENTALS

Lectures are Striver's A2Z linked-list playlist (L21–L23, L25–L27). Problems are LeetCode; LRU Cache is taught outside this playlist, so it ships without a solution video.

INVARIANT · LINKED LIST · THE HARDS ARE THE TRICKS, COMPOSED · DECK 3 OF 3
■ TOO SMALL TO READ
1280×720
INVARIANT · STEP 06 · DECK 3 OF 3

This one needs a laptop

Not a preference — an arithmetic one. Every slide is a fixed 1280 × 720 stage: a graph animating beside the code that drives it, with the problems laid out two columns wide. It scales as a single piece, so on a screen this size the body text comes out around 4px tall.

Shrinking it further would not help, and rebuilding it to reflow would mean losing the thing that makes it worth reading.

YOUR SCREEN0 × 0
NEEDED1060 × 610
WHAT IS WAITING ON THE LAPTOP
WATCH IT RUN, THEN RUN IT FROM MEMORY
Your progress is saved per device, so anything you tick on the laptop will be waiting there.