Hi👋SpeedAlgo • Deliberate Practice & Cognitive Ergonomics for Software Engineers

An interactive algorithm mastery and technical interview preparation platform published by Hi👋WebEnterprise. Built for senior and staff software engineers preparing for rigorous coding screens at top tech companies (FAANG/MAMAA).

13 Core Algorithmic Patterns & 225 Practice Problems

  • 1. Two Pointers (16 Paradigms, 43 Problems): Opposite ends, sorted pair sums, palindrome verification, trapping rain water, 3Sum, container with most water — includes the Sliding Window and Fast & Slow Pointers paradigms (contiguous subarray invariants, longest substrings, Floyd's cycle detection, linked list middle).
  • 2. Binary Search (9 Paradigms, 14 Problems): Monotonic predicate partitioning (feasible(x)), boundary search, rotated sorted arrays, square roots, minimum ship capacity, median of two sorted arrays, matrix median on value range.
  • 3. Bit Manipulation (5 Paradigms, 11 Problems): Counting set bits, XOR cancellation, bit reversal, bitmask pairing, maximum XOR with a bitwise trie.
  • 4. Math & Geometry (8 Paradigms, 14 Problems): Sieve of Eratosthenes, integer/roman conversions, modular arithmetic, geometric simulation.
  • 5. Tree/Graph Depth-First Search (15 Paradigms, 24 Problems): Path sums, lowest common ancestor, tree diameter, validating BSTs, tree DP (House Robber III, Binary Tree Cameras).
  • 6. Tree/Graph Breadth-First Search (5 Paradigms, 12 Problems): Level-order traversals, shortest path in unweighted graphs, zig-zag traversals, rotting oranges, word ladders.
  • 7. Graphs (12 Paradigms, 22 Problems): Topological sort, cycle detection, Dijkstra's algorithm, bipartite graph validation, network delay.
  • 8. Backtracking (6 Paradigms, 14 Problems): Subsets, permutations, combinations, constraint satisfaction, pruning, N-Queens, Sudoku solver.
  • 9. Dynamic Programming (13 Paradigms, 28 Problems): Memoization vs tabulation, 0/1 knapsack, unbounded knapsack, coin change, edit distance, longest common subsequence.
  • 10. Heap / Priority Queue (9 Paradigms, 11 Problems): Running medians, top-k elements, interval scheduling, IPO, k-way merges.
  • 11. Advanced Data Structures (7 Paradigms, 17 Problems): Trie (prefix tree), Union-Find (Disjoint Set Union), LRU Cache, LFU Cache, range-sum queries with updates.
  • 12. Stack (5 Paradigms, 9 Problems): Next greater element, largest rectangle in histogram, remove k digits, decode string, min stack.
  • 13. Intervals & Greedy (2 Paradigms, 6 Problems): Merge intervals, interval scheduling, meeting rooms by sweep line, jump game.

4-Stage Deliberate Practice Framework

  1. Stage 1 (Compare & Learn): Read the pattern's mental model, loop invariant and Big-O proof, compare the pattern's template with the problem's solution side by side, and use the step-by-step debugger. The solution can also be shown in C#, Java, TypeScript, C++, Go or Rust for almost every problem.
  2. Stage 2 (Active Recall): Rebuild the solution from memory in a code editor, with an invariant checklist and optional progressive hints, and check it against a judged test suite.
  3. Stage 3 (AI Interview Coach, coming soon): A mock-interview stage still in development; for now, students go straight from Stage 2 to Stage 4.
  4. Stage 4 (Solve on Your Own): Solve a new problem on your own against a 15-minute timer; your code is graded against a test suite. Python and TypeScript run in your browser and C# runs on our server (design problems grade in Python and TypeScript).

Equipped with a spaced-repetition review hub on a fixed 7-step schedule (1 day up to 90 days), a two-pane Studio workspace, and study notes.

Pricing, Access & Commercial Terms

  • Core Curriculum: 100% Free. No credit card required.
  • Coins: New accounts get 40 free coins once, the daily claim adds +20 coins once a day, and inviting a friend earns you +25 coins when they complete their first practice step. These are the only ways to earn coins.
  • AI Coach: Each AI coach question costs 1-5 coins, priced by the server according to its size.
  • BYOK (Bring Your Own Key): AI coaching is free with your own Google Gemini, OpenAI or OpenRouter API key (BYOK).
  • Extras: Coins also unlock extras for 24 hours: a Recall hint level (1 coin), a Solve solution reveal (2), a LeetCode problem card's solutions (1) and a pattern's extra canonical problems (1).
  • Payments & Refunds: Nothing on the site is sold for money: coins can only be earned, never bought. There are no subscriptions or charges, so there is nothing to refund.
  • Platform Operator: Hi👋WebEnterprise. Support & policies at hispeedalgo.com.
Skip to main content
Hi👋SpeedAlgo

Invariant-First Algorithmic Mastery

Arena
238Items
Theory Context•Two Pointers & Sliding Window
EasyLC 206

Reverse Linked List (LeetCode 206)

Target Frequency:AmazonAppleGoogleMetaMicrosoft

You get head, the first node of a singly linked list. Each node holds a value val and a pointer next to the node after it; the last node's next is None, and an empty list has head = None.

Turn the list around: the node that was last must come first, and every node must point to the one that used to come before it. Return the first node of the reversed list (None for an empty list, written []).

Follow-up: once you have one version, write the other one too, a loop and a recursive function.

Worked Examples

Example 1
Input:head = [1,2,3,4,5]
Output:[5,4,3,2,1]
12345
Explanation: Every link now points the other way, so 5, the old last node, is the new head and 1 ends the list.
Example 2
Input:head = [1,2]
Output:[2,1]
12
Explanation: 2 now points to 1, and 1 points to None.
Example 3
Input:head = []
Output:[]
Explanation: An empty list has nothing to turn around, so the answer is the empty list.

⚖️Formal Constraints & Bounds

  • The number of nodes in the list is in the range [0, 5000].

  • -5000 <= Node.val <= 5000

Deep-Dive & Conceptual Insights

Core Insight

Only the direction of each next pointer changes, so one forward walk that saves the next node, flips the current link and steps on reverses the whole list in place, without creating or copying a single node.

Real-World Scenario & Production Applications

Many systems keep history as a chain where each entry points to the one before it: an undo log, a version chain, a free list inside a memory allocator. To replay such a chain oldest-first you need the links pointing the other way. When the next pointer lives inside the object itself (an intrusive list, common in operating-system kernels), flipping the links in place does this without allocating a second list.

Step-by-Step Execution Trace Table

Input head = [1,2,3,4,5] (LeetCode Example 1). Before each step, prev heads the reversed part and curr heads the untouched part:

Stepcurrnxt = curr.nextcurr.next = prevReversed part (from prev) after the stepUntouched part (from curr) after the step
1121 -> None1 -> None2 -> 3 -> 4 -> 5 -> None
2232 -> 12 -> 1 -> None3 -> 4 -> 5 -> None
3343 -> 23 -> 2 -> 1 -> None4 -> 5 -> None
4454 -> 34 -> 3 -> 2 -> 1 -> None5 -> None
55None5 -> 45 -> 4 -> 3 -> 2 -> 1 -> Noneempty (curr is None)
Endreturn prev: [5,4,3,2,1]
Code Shape
Archetype Code Shape
Python
1Only the direction of each `next` pointer changes: no node moves and no value is copied, so one forward walk can flip the links one at a time.
2Keep two lists apart: `prev` heads the nodes already reversed (empty at first) and `curr` heads the nodes not touched yet; each step moves `curr` to the front of `prev`'s list.
3`prev, curr = None, head`; `while curr:` save `nxt = curr.next`, flip `curr.next = prev`, then `prev = curr` and `curr = nxt`; after the loop `return prev`.
4The trap: `nxt = curr.next` must run before `curr.next = prev`. The flip overwrites the only link to the rest of the list, so flipping first loses every node after `curr`.

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

A singly linked list is a chain of nodes where each node knows only the node after it (next). To reverse it you don't move any node and you don't copy any value: you make every next point to the node that used to come before it. The list can only be walked forward, so you do it in one forward walk, carrying two pointers: prev, the head of the part already reversed, and curr, the first node not touched yet.

🏟️ The Analogy: Turning Around a Line of People Holding Shoulders

Picture a queue where everyone rests a hand on the shoulder of the person in front. To turn the whole line around, you go from the front to the back and ask each person to move their hand to the person behind them instead. Before someone lets go, you must note who they were holding, or you lose track of the rest of the line. That note is nxt.

🔑 Why It Works

Before every loop test, the nodes split into two separate lists: the reversed part starting at prev and the untouched part starting at curr. Each step moves exactly one node from the front of the untouched part to the front of the reversed part. The order of the four lines is the whole trick: curr.next = prev destroys the only link to the rest of the list, so nxt = curr.next must come first.

  • Flipping before saving: nxt = curr.next must run before curr.next = prev. The flip overwrites the only link to the rest of the list, so flipping first loses every node after curr: [1,2,3] comes back as [1].

  • Returning the wrong node: return prev, not head or curr. When the loop ends curr is None, and the old head is now the tail, pointing at None.

  • Stopping one node early: loop while curr:, not while curr.next:. The shorter loop never flips the last node, and the empty list (head is None) crashes on None.next.

  • Starting prev at head: start with prev = None. The old head becomes the tail and must end pointing at None; starting from head without clearing head.next leaves the first two nodes pointing at each other, a cycle.

  • Copying instead of rewiring: reading the values into a Python list and building new nodes gives the right values but uses O(N) extra memory; the four-line loop reuses the original nodes with O(1) extra space.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"Reverse the list" and "return the reversed list": the nodes stay the same and only their order changes, and a singly linked list can only be walked forward. The need is to point every next the other way during one forward walk, in place: the Linked List Reversal tool.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Before each loop test, prev is the head of the nodes already reversed and curr is the head of the nodes not touched yet, and every node is in exactly one of the two lists. Each step moves one node, curr, from the front of the untouched part to the front of the reversed part.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

This problem's traps are in Common Pitfalls above.

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd use Linked List Reversal. The nodes stay where they are; only each next pointer has to point the other way, so I walk the list once with two pointers. prev is the head of the part I've already reversed, starting as None, and curr is the first node I haven't touched. At each step I save curr.next in nxt, point curr.next back at prev, then move prev to curr and curr to nxt. Every node moves from the untouched part to the front of the reversed part exactly once, so when curr runs off the end, prev is the new head. The trap is the order of those lines: if I flip before saving nxt, I lose the rest of the list. That's O(N) time and O(1) extra space, and no node is created.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N)

O(N): the while curr: loop runs once per node with constant work inside, and nothing else in the code depends on N.

SPACE COMPLEXITY

O(1)

The code creates no node and no list: it only rewrites the next field that every node already has. The extra memory is three pointers, prev, curr and nxt, whatever the length of the list, so O(1). The returned list is the input's own nodes, so there is no separate output to count.

Formal Recurrence Relation

T = O(1) setup + N · O(1) per node = O(N)

Derivation Progression

Set up

O(1)

prev = None and curr = head run once.

Visit every node

N iterations

Each pass of while curr: ends with curr = nxt, one node forward, so the loop body runs exactly once per node.

Flip one link

O(1) per node

nxt = curr.next, curr.next = prev, prev = curr, curr = nxt: four pointer assignments, no inner loop.

Total

O(N)

N iterations of constant work, plus the O(1) return prev.

Variable Definitions

NNN

Number of nodes in the list (0 to 5000)

prevprevprev

Head of the part already reversed (None at the start)

currcurrcurr

First node not flipped yet (None once every node is done)

nxtnxtnxt

curr.next, saved before the flip overwrites it

Memory Architecture & Bounds

🟣 Call Stack

O(1) Iterative, no recursion (a recursive version would use an O(N) call stack)

🔵 Auxiliary Heap

O(1): three pointers, prev, curr and nxt; no node is created

🟢 Output Space

None extra: the returned list is the input's own nodes, rewired

Boundary Best / Worst Cases

Best Case

O(1)O(1)O(1): an empty list never enters the loop

Average Case

O(N)O(N)O(N)

Worst Case

O(N)O(N)O(N): every node is visited once

Pointer Invariant Transition Progression

Pointer Invariant Transition Progression
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "reverse the list", "return the reversed list". The nodes stay the same and only the order of the links changes, and a singly linked list can only be walked forward: Linked List Reversal, flipping each next during one walk.

CONSTRAINTS & BOUNDS

0≤N≤50000 \le N \le 50000≤N≤5000 nodes, values in [−5000,5000][-5000, 5000][−5000,5000]. Any O(N)O(N)O(N) approach fits; the point is memory: copying the values costs O(N)O(N)O(N) extra, rewiring in place costs O(1)O(1)O(1). A recursive version is also O(N)O(N)O(N) time but puts NNN frames on the call stack.

FAANG PRODUCTION TRAPS & EDGE CASES

At scale: a recursive reversal puts one stack frame per node, so a long list can overflow the call stack (Python's default limit is about 1000 frames), which the loop never does.

Core Algorithmic State Invariants

  1. Short Lists Need No Special Case

Because prev starts as None, an empty list skips the loop and returns None, and a one-node list runs it once, setting head.next = None (it already was) and returning head. The same four lines cover every length, so no if is needed before the loop.

  1. Where the Loop Stops Is Where the Segment Reconnects

In reverse_between, the counted loop stops with prev on the segment's new first node and curr on the first node after the segment. Those are exactly the two ends that before.next = prev and tail.next = curr need, so no second walk is needed to find them.

Theory Context•Two Pointers & Sliding Window
EasyLC 206

Reverse Linked List (LeetCode 206)

Target Frequency:AmazonAppleGoogleMetaMicrosoft

You get head, the first node of a singly linked list. Each node holds a value val and a pointer next to the node after it; the last node's next is None, and an empty list has head = None.

Turn the list around: the node that was last must come first, and every node must point to the one that used to come before it. Return the first node of the reversed list (None for an empty list, written []).

Follow-up: once you have one version, write the other one too, a loop and a recursive function.

Worked Examples

Example 1
Input:head = [1,2,3,4,5]
Output:[5,4,3,2,1]
12345
Explanation: Every link now points the other way, so 5, the old last node, is the new head and 1 ends the list.
Example 2
Input:head = [1,2]
Output:[2,1]
12
Explanation: 2 now points to 1, and 1 points to None.
Example 3
Input:head = []
Output:[]
Explanation: An empty list has nothing to turn around, so the answer is the empty list.

⚖️Formal Constraints & Bounds

  • The number of nodes in the list is in the range [0, 5000].

  • -5000 <= Node.val <= 5000

Deep-Dive & Conceptual Insights

Core Insight

Only the direction of each next pointer changes, so one forward walk that saves the next node, flips the current link and steps on reverses the whole list in place, without creating or copying a single node.

Real-World Scenario & Production Applications

Many systems keep history as a chain where each entry points to the one before it: an undo log, a version chain, a free list inside a memory allocator. To replay such a chain oldest-first you need the links pointing the other way. When the next pointer lives inside the object itself (an intrusive list, common in operating-system kernels), flipping the links in place does this without allocating a second list.

Step-by-Step Execution Trace Table

Input head = [1,2,3,4,5] (LeetCode Example 1). Before each step, prev heads the reversed part and curr heads the untouched part:

Stepcurrnxt = curr.nextcurr.next = prevReversed part (from prev) after the stepUntouched part (from curr) after the step
1121 -> None1 -> None2 -> 3 -> 4 -> 5 -> None
2232 -> 12 -> 1 -> None3 -> 4 -> 5 -> None
3343 -> 23 -> 2 -> 1 -> None4 -> 5 -> None
4454 -> 34 -> 3 -> 2 -> 1 -> None5 -> None
55None5 -> 45 -> 4 -> 3 -> 2 -> 1 -> Noneempty (curr is None)
Endreturn prev: [5,4,3,2,1]
Code Shape
Archetype Code Shape
Python
1Only the direction of each `next` pointer changes: no node moves and no value is copied, so one forward walk can flip the links one at a time.
2Keep two lists apart: `prev` heads the nodes already reversed (empty at first) and `curr` heads the nodes not touched yet; each step moves `curr` to the front of `prev`'s list.
3`prev, curr = None, head`; `while curr:` save `nxt = curr.next`, flip `curr.next = prev`, then `prev = curr` and `curr = nxt`; after the loop `return prev`.
4The trap: `nxt = curr.next` must run before `curr.next = prev`. The flip overwrites the only link to the rest of the list, so flipping first loses every node after `curr`.

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

A singly linked list is a chain of nodes where each node knows only the node after it (next). To reverse it you don't move any node and you don't copy any value: you make every next point to the node that used to come before it. The list can only be walked forward, so you do it in one forward walk, carrying two pointers: prev, the head of the part already reversed, and curr, the first node not touched yet.

🏟️ The Analogy: Turning Around a Line of People Holding Shoulders

Picture a queue where everyone rests a hand on the shoulder of the person in front. To turn the whole line around, you go from the front to the back and ask each person to move their hand to the person behind them instead. Before someone lets go, you must note who they were holding, or you lose track of the rest of the line. That note is nxt.

🔑 Why It Works

Before every loop test, the nodes split into two separate lists: the reversed part starting at prev and the untouched part starting at curr. Each step moves exactly one node from the front of the untouched part to the front of the reversed part. The order of the four lines is the whole trick: curr.next = prev destroys the only link to the rest of the list, so nxt = curr.next must come first.

  • Flipping before saving: nxt = curr.next must run before curr.next = prev. The flip overwrites the only link to the rest of the list, so flipping first loses every node after curr: [1,2,3] comes back as [1].

  • Returning the wrong node: return prev, not head or curr. When the loop ends curr is None, and the old head is now the tail, pointing at None.

  • Stopping one node early: loop while curr:, not while curr.next:. The shorter loop never flips the last node, and the empty list (head is None) crashes on None.next.

  • Starting prev at head: start with prev = None. The old head becomes the tail and must end pointing at None; starting from head without clearing head.next leaves the first two nodes pointing at each other, a cycle.

  • Copying instead of rewiring: reading the values into a Python list and building new nodes gives the right values but uses O(N) extra memory; the four-line loop reuses the original nodes with O(1) extra space.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"Reverse the list" and "return the reversed list": the nodes stay the same and only their order changes, and a singly linked list can only be walked forward. The need is to point every next the other way during one forward walk, in place: the Linked List Reversal tool.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Before each loop test, prev is the head of the nodes already reversed and curr is the head of the nodes not touched yet, and every node is in exactly one of the two lists. Each step moves one node, curr, from the front of the untouched part to the front of the reversed part.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

This problem's traps are in Common Pitfalls above.

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd use Linked List Reversal. The nodes stay where they are; only each next pointer has to point the other way, so I walk the list once with two pointers. prev is the head of the part I've already reversed, starting as None, and curr is the first node I haven't touched. At each step I save curr.next in nxt, point curr.next back at prev, then move prev to curr and curr to nxt. Every node moves from the untouched part to the front of the reversed part exactly once, so when curr runs off the end, prev is the new head. The trap is the order of those lines: if I flip before saving nxt, I lose the rest of the list. That's O(N) time and O(1) extra space, and no node is created.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N)

O(N): the while curr: loop runs once per node with constant work inside, and nothing else in the code depends on N.

SPACE COMPLEXITY

O(1)

The code creates no node and no list: it only rewrites the next field that every node already has. The extra memory is three pointers, prev, curr and nxt, whatever the length of the list, so O(1). The returned list is the input's own nodes, so there is no separate output to count.

Formal Recurrence Relation

T = O(1) setup + N · O(1) per node = O(N)

Derivation Progression

Set up

O(1)

prev = None and curr = head run once.

Visit every node

N iterations

Each pass of while curr: ends with curr = nxt, one node forward, so the loop body runs exactly once per node.

Flip one link

O(1) per node

nxt = curr.next, curr.next = prev, prev = curr, curr = nxt: four pointer assignments, no inner loop.

Total

O(N)

N iterations of constant work, plus the O(1) return prev.

Variable Definitions

NNN

Number of nodes in the list (0 to 5000)

prevprevprev

Head of the part already reversed (None at the start)

currcurrcurr

First node not flipped yet (None once every node is done)

nxtnxtnxt

curr.next, saved before the flip overwrites it

Memory Architecture & Bounds

🟣 Call Stack

O(1) Iterative, no recursion (a recursive version would use an O(N) call stack)

🔵 Auxiliary Heap

O(1): three pointers, prev, curr and nxt; no node is created

🟢 Output Space

None extra: the returned list is the input's own nodes, rewired

Boundary Best / Worst Cases

Best Case

O(1)O(1)O(1): an empty list never enters the loop

Average Case

O(N)O(N)O(N)

Worst Case

O(N)O(N)O(N): every node is visited once

Pointer Invariant Transition Progression

Pointer Invariant Transition Progression
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "reverse the list", "return the reversed list". The nodes stay the same and only the order of the links changes, and a singly linked list can only be walked forward: Linked List Reversal, flipping each next during one walk.

CONSTRAINTS & BOUNDS

0≤N≤50000 \le N \le 50000≤N≤5000 nodes, values in [−5000,5000][-5000, 5000][−5000,5000]. Any O(N)O(N)O(N) approach fits; the point is memory: copying the values costs O(N)O(N)O(N) extra, rewiring in place costs O(1)O(1)O(1). A recursive version is also O(N)O(N)O(N) time but puts NNN frames on the call stack.

FAANG PRODUCTION TRAPS & EDGE CASES

At scale: a recursive reversal puts one stack frame per node, so a long list can overflow the call stack (Python's default limit is about 1000 frames), which the loop never does.

Core Algorithmic State Invariants

  1. Short Lists Need No Special Case

Because prev starts as None, an empty list skips the loop and returns None, and a one-node list runs it once, setting head.next = None (it already was) and returning head. The same four lines cover every length, so no if is needed before the loop.

  1. Where the Loop Stops Is Where the Segment Reconnects

In reverse_between, the counted loop stops with prev on the segment's new first node and curr on the first node after the segment. Those are exactly the two ends that before.next = prev and tail.next = curr need, so no second walk is needed to find them.

© 2026 Hi👋WebEnterprise. All rights reserved.
Sitemap•llms.txt•