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).

12 Core Algorithmic Patterns & 189 Practice Problems

  • 1. Two Pointers (10 Paradigms, 34 Problems): Converging pointers, sorted pair sums, container with most water, trapping rain water, 3Sum, plus the Sliding Window and Fast & Slow Pointers paradigms (Floyd cycle detection, monotonic window invariants, longest substrings, minimum window).
  • 2. Binary Search (8 Paradigms, 12 Problems): Monotonic predicate partitioning, boundary searching, rotated arrays, median of two sorted arrays, matrix median on value range.
  • 3. Bit Manipulation (5 Paradigms, 8 Problems): Bitmasking, XOR tricks, counting set bits, subset enumeration via bitmasks.
  • 4. Math & Geometry (7 Paradigms, 13 Problems): Sieve of Eratosthenes, integer/roman conversions, modular arithmetic, geometric simulation.
  • 5. Tree/Graph Depth-First Search (10 Paradigms, 19 Problems): Path sums, lowest common ancestor, tree diameter, subtree serialization, validating BSTs.
  • 6. Tree/Graph Breadth-First Search (4 Paradigms, 11 Problems): Level-order traversals, shortest path, rotting oranges, word ladders.
  • 7. Graphs (10 Paradigms, 18 Problems): Topological sort, cycle detection, Dijkstra, bipartite validation, network delay.
  • 8. Backtracking (6 Paradigms, 14 Problems): Subsets, permutations, combinations, constraint satisfaction, pruning, N-Queens.
  • 9. Dynamic Programming (11 Paradigms, 18 Problems): Memoization vs tabulation, knapsack, coin change, edit distance, longest common subsequence.
  • 10. Heap / Priority Queue (8 Paradigms, 10 Problems): Running medians, top-k elements, interval scheduling, IPO, k-way merges.
  • 11. Advanced Data Structures (6 Paradigms, 14 Problems): Trie, Union-Find, LRU Cache, LFU Cache, Monotonic Stacks.
  • 12. Intervals & Stack / Miscellaneous (11 Paradigms, 18 Problems): Merge intervals, daily temperatures, largest rectangle in histogram, trapping rain water via stack.

4-Stage Deliberate Practice Framework

  1. Stage 1 (Compare & Learn): Multi-language Rosetta Stone contrasting abstract invariants with concrete solutions across Python, C#, Java, TypeScript, C++, Go, and Rust.
  2. Stage 2 (Active Recall): Reconstruct algorithmic template invariants from memory with real-time feedback before looking at solutions.
  3. Stage 3 (Senior SWE AI Mock Coach): Simulated senior mock interview evaluating Big-O space/time tradeoffs, edge cases, and code reviews in Monaco Editor.
  4. Stage 4 (Solve on Your Own): Timed sandbox challenges verified against automated test suites in Python, C#, Java, and TypeScript.

Equipped with SM-2 Spaced Repetition Review Hub, Studio Cockpit workspace layout, and interactive study notes.

Pricing, Access & Commercial Terms

  • Core Curriculum: 100% Free. No credit card required.
  • Compute Coins: 40 free coins upon signup, +20 daily login bonus, +25 referral bonus.
  • 24-Hour AI Coaching Pass: 5 compute coins unlocks unlimited senior SWE AI coaching for a full 24 hours.
  • BYOK (Bring Your Own Key): Completely free unlimited AI coaching if using your own Gemini/OpenAI API key.
  • Refund & Subscription Policy: No recurring charges, no subscription traps, and no paid paywalls. Free tier provides full learning path.
  • Platform Operator: Hi👋WebEnterprise Inc. Support & policies at hispeedalgo.com.
Skip to main content
Hi👋SpeedAlgo

Invariant-First Algorithmic Mastery

201Items
Theory Context•Math & Geometry
MediumLC 382

Linked List Random Node (LeetCode 382)

You will see how one walk and one random draw per node pick a uniform random node without knowing the length.

Target Frequency:GoogleMetaAmazon

Build a class, Solution, around a singly linked list, that hands back the value of a node picked at random.

  • Solution(ListNode head) receives the first node of the list.
  • int getRandom() returns the value of one node of the list, chosen so that every node has the same chance. Each call makes a new, independent choice.

Nodes may hold equal values, so a value stored in two nodes comes back twice as often as a value stored in one. Any value that is in the list is a correct single answer; long runs of calls are checked for an even spread over the nodes. LeetCode's follow-up asks what to do when the list is too long to store and its length is unknown, and whether you can pick with no extra space.

Worked Examples

Example 1
Input:["Solution", "getRandom", "getRandom", "getRandom", "getRandom", "getRandom"] [[[1, 2, 3]], [], [], [], [], []]
Output:[null, 1, 3, 2, 2, 3]
123
Explanation: The list is `1 -> 2 -> 3`. Each `getRandom()` returns 1, 2 or 3, each with chance 1/3, so many other outputs are just as correct; LeetCode shows one possible run.

⚖️Formal Constraints & Bounds

  • The number of nodes in the linked list will be in the range [1, 104].

  • -104 <= Node.val <= 104

  • At most 104 calls will be made to getRandom.

Deep-Dive & Conceptual Insights

Why It Works & Core Invariant

Keep the i-th node with chance exactly 1/i. The new node gets in with 1/i, and each earlier node, which held the spot with 1/(i - 1), survives with (i - 1)/i, which is also 1/i: after the last node every node has chance 1/n, with no length and no copy.

Real-World Scenario & Production Applications

A log or metrics pipeline that keeps a uniform sample of events from a stream it can read only once and whose length it never knows, such as picking a random request to trace from millions that flow past, with one slot of memory per sample.

Subproblems & Recurrence Decomposition3 Phases

🧩Subproblem 1: Mathematical Boundary & State Invariant

Establish 4-wall perimeter bounding (top, bottom, left, right) or binary exponent halving state x^n = (x^2)^(n/2).

Mathematical Recurrence / Code Invariant
top, bottom = 0, R - 1
left, right = 0, C - 1
res = []

Step-by-Step Execution Trace Table

One call of getRandom() on 1 -> 2 -> 3, with the draws randrange(1) = 0, randrange(2) = 1, randrange(3) = 0:

StepNodeiDraw random.randrange(i)Kept?resultChance each node seen so far is result
1110yes (always, 1/1)11: 1
2221no (chance 1/2)11: 1/2, 2: 1/2
3330yes (chance 1/3)31: 1/3, 2: 1/3, 3: 1/3
Scroll horizontally to see all columns, or expand to full screen

The call returns 3. Another call makes new draws, and each of the three values comes back one time in three.

Core Invariant Specification & Code Shape
Archetype Code Shape
Python
1The length is unknown until the walk ends, so decide as you go: keep one candidate `result`, and give each new node a chance to replace it.
2Keep this true: after the walk has seen `i` nodes, each of them is `result` with the same chance, 1/i.
3One pass over the list per call, counting the nodes; at each node one random draw decides whether it replaces `result`; return `result` after the last node.
4The trap: count this node before the draw and keep it with chance exactly 1/i. A chance of 1/(i + 1) keeps the head only half the time and can return the starting 0.

Target: Linked List Random Node (LeetCode 382). No copy of the values: every pick walks the list again, so the extra memory stays O(1).

Boundary Model: 4-Pointer Boundary Box Contraction [top, bottom, left, right]

Boundary pointers contract inward after each directional sweep; modular arithmetic bounds state cyclically.

Loop Invariant Termination

while top <= bottom and left <= right: sweep right, down, left, up, contracting respective pointer.

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

The easy answer copies the values into an array and picks a random index, but that needs O(N) extra memory and the whole list up front; LeetCode's follow-up takes both away. Reservoir Sampling picks while it walks: keep one candidate, result, count the nodes with i, and let the i-th node replace result with chance exactly 1/i, drawn as random.randrange(i) == 0. When the walk ends, every node has had the same chance to be the one left in result.

🎟️ The Analogy: A Raffle Where Tickets Keep Arriving

People walk into a room one at a time, and one prize must end up with a random person, but nobody knows how many will come. The first person holds the prize. When the second arrives, a coin decides whether the prize moves to them. When the third arrives, a three-sided die decides; the i-th newcomer takes the prize with chance 1/i. Whoever holds it when the door closes won a fair raffle: each newcomer got in with 1/i, and everyone already in the room kept it with exactly the chance that makes all shares equal.

🪄 The Mathematical Harmony / Magic Trick
Code / Blueprint
i += 1
if random.randrange(i) == 0:
result = node.val
 

Claim: after i nodes, each of them is result with chance 1/i. It holds for i = 1, because randrange(1) is always 0. If it holds for i - 1, the new node gets in with 1/i, and each earlier node, result with chance 1/(i - 1), is not replaced with chance (i - 1)/i, so it stays with 1/(i - 1) · (i - 1)/i = 1/i. The counter must include the current node before the draw; randrange(i + 1) would give the head only a 1/2 chance and could leave result at its starting 0.

💡 Summary

One walk per call: i += 1, keep the node when random.randrange(i) == 0, and never stop early. O(N)O(N)O(N) time per getRandom, O(1)O(1)O(1) extra space, and no length needed.

  • The wrong chance: count this node before the draw and keep it when random.randrange(i) == 0, a chance of exactly 1/i. randrange(i + 1) keeps [1, 2]'s 1 only a third of the time and returns the starting 0 another third.

  • Stopping at the first hit: keep walking after a replacement. The first node's chance is 1/1, so returning at the first successful draw always returns the head.

  • Drawing once: draw inside getRandom, on every call. An index chosen once in __init__ returns the same value every time.

  • Copying the list: an array plus random.choice also picks fairly, but needs O(N) extra memory and the length up front, which the follow-up's stream does not give.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

You will see how a senior engineer hears "every node has the same chance" plus "length unknown" and reaches for a reservoir.

Pattern Recognition Signals

The 10-second spot

"Every node has the same chance", "each call makes a new, independent choice", and the follow-up "the list is too long to store and its length is unknown" with "no extra space": a uniform pick from a sequence read once, front to back. That is the signal for Reservoir Sampling: keep one candidate and let the i-th item replace it with chance 1/i.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

After the walk has seen i nodes, each of them is result with chance exactly 1/i; the draw random.randrange(i) == 0 keeps the i-th node with chance 1/i, which is exactly what leaves every earlier node at 1/i too.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • Count the node before the draw and keep it when random.randrange(i) == 0: randrange(i + 1) keeps [1, 2]'s 1 only a third of the time and returns the starting 0 another third.

  • Keep walking after a replacement: the first node's chance is 1/1, so returning at the first hit always returns the head.

  • Draw inside getRandom, on every call: an index chosen once in __init__ returns the same value every time.

  • Don't copy the list: an array plus random.choice is fair, but uses O(N) extra memory and needs the length, which the follow-up takes away.

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd use Reservoir Sampling. I don't know the length and I don't want to copy the list, so I pick while I walk. I keep one candidate, result, and a counter i. At each node I add one to i, and I replace result with this node's value when a random number from zero to i minus one is zero, so with chance exactly one over i. That's fair: the new node gets in with one over i, and every earlier node, which held the spot with one over i minus one, survives with i minus one over i, which leaves it at one over i as well. The trap is the counter: it has to include this node before the draw, otherwise the head only gets a half and the answer can stay at zero. And I never stop early. That's O(N) time per call and O(1) extra space.

So: walk once per call, count the nodes, keep the i-th with chance 1/i, and never stop early.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N) per getRandom

__init__ only stores head: O(1). Each getRandom runs while node: once per node, N times, and each pass does one i += 1, one random.randrange(i) draw (O(1)), at most one assignment to result, and one step node = node.next. Nothing else loops, so a call costs O(N). A copy into an array would make each pick O(1) after an O(N) copy, but the win here is memory, not time.

SPACE COMPLEXITY

O(1)

The object keeps one reference, self.head, and getRandom keeps result, i and node: O(1) extra space, whatever the length. The list itself is the input and is never copied.

Formal Recurrence Relation

T(N) = N · (one increment + one draw + one step) = O(N) per call

__init__ only stores head: O(1). Each getRandom runs while node: once per node, N times, and each pass does one i += 1, one random.randrange(i) draw (O(1)), at most one assignment to result, and one step node = node.next. Nothing else loops, so a call costs O(N). A copy into an array would make each pick O(1) after an O(N) copy, but the win here is memory, not time.

Derivation Progression

Store the head

O(1)

__init__ keeps one reference and copies nothing.

One pass per call

N

while node: visits each node once and never stops early.

Work per node

O(1)

i += 1, one random.randrange(i) draw, at most one assignment, and node = node.next.

Total per getRandom

O(N)

N constant-time steps; k calls cost O(k · N).

Variable Definitions

NNN

Number of nodes in the list (at most 10^4)

Memory Architecture & Bounds

🟣 Call Stack

O(1): no recursion

🔵 Auxiliary Heap

O(1): self.head, result, i, node

🟢 Output Space

O(1): one integer per call

Boundary Best / Worst Cases

Best Case

O(N)O(N)O(N): every call walks the whole list

Average Case

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

Worst Case

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

Recurrence Tree Topology

Recurrence Tree Topology
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "every node has the same chance", "each call makes a new, independent choice", "its length is unknown". A uniform pick from a sequence read once with no room to store it: Reservoir Sampling, keep the i-th item with chance 1/i.

CONSTRAINTS & BOUNDS

Up to 10410^4104 nodes and 10410^4104 calls: one walk per call is up to 10810^8108 steps in the worst case, the price of O(1) extra space. With memory to spare, an array of the 10410^4104 values makes each pick O(1).

FAANG PRODUCTION TRAPS & EDGE CASES

The draw must be unbiased for every i: rand() % i in C carries a small bias when RAND_MAX + 1 is not a multiple of i, so production code uses an unbiased bounded generator. Sampling k items instead of one keeps a reservoir of k and replaces a random slot with chance k/i. On a stream split across machines, each worker keeps its own reservoir with its count, and the reservoirs are merged by picking from each in proportion to its count.

Core Algorithmic State Invariants

1. The i-th Item Gets Chance 1/i

`random.randrange(i) == 0` is true with chance exactly 1/i, and `i` already counts the current node, so the first node is always taken and every later one gets its fair share.

2. Every Earlier Item Stays at 1/i

An item that was `result` with chance 1/(i - 1) survives the i-th draw with (i - 1)/i, so it stays with 1/i: the shares stay equal at every step, and the walk must never stop early.

3. O(N) per Pick, O(1) Space

One walk per call with three variables: no copy of the list and no length needed, at the cost of O(N) time per `getRandom`.

Theory Context•Math & Geometry
MediumLC 382

Linked List Random Node (LeetCode 382)

You will see how one walk and one random draw per node pick a uniform random node without knowing the length.

Target Frequency:GoogleMetaAmazon

Build a class, Solution, around a singly linked list, that hands back the value of a node picked at random.

  • Solution(ListNode head) receives the first node of the list.
  • int getRandom() returns the value of one node of the list, chosen so that every node has the same chance. Each call makes a new, independent choice.

Nodes may hold equal values, so a value stored in two nodes comes back twice as often as a value stored in one. Any value that is in the list is a correct single answer; long runs of calls are checked for an even spread over the nodes. LeetCode's follow-up asks what to do when the list is too long to store and its length is unknown, and whether you can pick with no extra space.

Worked Examples

Example 1
Input:["Solution", "getRandom", "getRandom", "getRandom", "getRandom", "getRandom"] [[[1, 2, 3]], [], [], [], [], []]
Output:[null, 1, 3, 2, 2, 3]
123
Explanation: The list is `1 -> 2 -> 3`. Each `getRandom()` returns 1, 2 or 3, each with chance 1/3, so many other outputs are just as correct; LeetCode shows one possible run.

⚖️Formal Constraints & Bounds

  • The number of nodes in the linked list will be in the range [1, 104].

  • -104 <= Node.val <= 104

  • At most 104 calls will be made to getRandom.

Deep-Dive & Conceptual Insights

Why It Works & Core Invariant

Keep the i-th node with chance exactly 1/i. The new node gets in with 1/i, and each earlier node, which held the spot with 1/(i - 1), survives with (i - 1)/i, which is also 1/i: after the last node every node has chance 1/n, with no length and no copy.

Real-World Scenario & Production Applications

A log or metrics pipeline that keeps a uniform sample of events from a stream it can read only once and whose length it never knows, such as picking a random request to trace from millions that flow past, with one slot of memory per sample.

Subproblems & Recurrence Decomposition3 Phases

🧩Subproblem 1: Mathematical Boundary & State Invariant

Establish 4-wall perimeter bounding (top, bottom, left, right) or binary exponent halving state x^n = (x^2)^(n/2).

Mathematical Recurrence / Code Invariant
top, bottom = 0, R - 1
left, right = 0, C - 1
res = []

Step-by-Step Execution Trace Table

One call of getRandom() on 1 -> 2 -> 3, with the draws randrange(1) = 0, randrange(2) = 1, randrange(3) = 0:

StepNodeiDraw random.randrange(i)Kept?resultChance each node seen so far is result
1110yes (always, 1/1)11: 1
2221no (chance 1/2)11: 1/2, 2: 1/2
3330yes (chance 1/3)31: 1/3, 2: 1/3, 3: 1/3
Scroll horizontally to see all columns, or expand to full screen

The call returns 3. Another call makes new draws, and each of the three values comes back one time in three.

Core Invariant Specification & Code Shape
Archetype Code Shape
Python
1The length is unknown until the walk ends, so decide as you go: keep one candidate `result`, and give each new node a chance to replace it.
2Keep this true: after the walk has seen `i` nodes, each of them is `result` with the same chance, 1/i.
3One pass over the list per call, counting the nodes; at each node one random draw decides whether it replaces `result`; return `result` after the last node.
4The trap: count this node before the draw and keep it with chance exactly 1/i. A chance of 1/(i + 1) keeps the head only half the time and can return the starting 0.

Target: Linked List Random Node (LeetCode 382). No copy of the values: every pick walks the list again, so the extra memory stays O(1).

Boundary Model: 4-Pointer Boundary Box Contraction [top, bottom, left, right]

Boundary pointers contract inward after each directional sweep; modular arithmetic bounds state cyclically.

Loop Invariant Termination

while top <= bottom and left <= right: sweep right, down, left, up, contracting respective pointer.

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

The easy answer copies the values into an array and picks a random index, but that needs O(N) extra memory and the whole list up front; LeetCode's follow-up takes both away. Reservoir Sampling picks while it walks: keep one candidate, result, count the nodes with i, and let the i-th node replace result with chance exactly 1/i, drawn as random.randrange(i) == 0. When the walk ends, every node has had the same chance to be the one left in result.

🎟️ The Analogy: A Raffle Where Tickets Keep Arriving

People walk into a room one at a time, and one prize must end up with a random person, but nobody knows how many will come. The first person holds the prize. When the second arrives, a coin decides whether the prize moves to them. When the third arrives, a three-sided die decides; the i-th newcomer takes the prize with chance 1/i. Whoever holds it when the door closes won a fair raffle: each newcomer got in with 1/i, and everyone already in the room kept it with exactly the chance that makes all shares equal.

🪄 The Mathematical Harmony / Magic Trick
Code / Blueprint
i += 1
if random.randrange(i) == 0:
result = node.val
 

Claim: after i nodes, each of them is result with chance 1/i. It holds for i = 1, because randrange(1) is always 0. If it holds for i - 1, the new node gets in with 1/i, and each earlier node, result with chance 1/(i - 1), is not replaced with chance (i - 1)/i, so it stays with 1/(i - 1) · (i - 1)/i = 1/i. The counter must include the current node before the draw; randrange(i + 1) would give the head only a 1/2 chance and could leave result at its starting 0.

💡 Summary

One walk per call: i += 1, keep the node when random.randrange(i) == 0, and never stop early. O(N)O(N)O(N) time per getRandom, O(1)O(1)O(1) extra space, and no length needed.

  • The wrong chance: count this node before the draw and keep it when random.randrange(i) == 0, a chance of exactly 1/i. randrange(i + 1) keeps [1, 2]'s 1 only a third of the time and returns the starting 0 another third.

  • Stopping at the first hit: keep walking after a replacement. The first node's chance is 1/1, so returning at the first successful draw always returns the head.

  • Drawing once: draw inside getRandom, on every call. An index chosen once in __init__ returns the same value every time.

  • Copying the list: an array plus random.choice also picks fairly, but needs O(N) extra memory and the length up front, which the follow-up's stream does not give.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

You will see how a senior engineer hears "every node has the same chance" plus "length unknown" and reaches for a reservoir.

Pattern Recognition Signals

The 10-second spot

"Every node has the same chance", "each call makes a new, independent choice", and the follow-up "the list is too long to store and its length is unknown" with "no extra space": a uniform pick from a sequence read once, front to back. That is the signal for Reservoir Sampling: keep one candidate and let the i-th item replace it with chance 1/i.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

After the walk has seen i nodes, each of them is result with chance exactly 1/i; the draw random.randrange(i) == 0 keeps the i-th node with chance 1/i, which is exactly what leaves every earlier node at 1/i too.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • Count the node before the draw and keep it when random.randrange(i) == 0: randrange(i + 1) keeps [1, 2]'s 1 only a third of the time and returns the starting 0 another third.

  • Keep walking after a replacement: the first node's chance is 1/1, so returning at the first hit always returns the head.

  • Draw inside getRandom, on every call: an index chosen once in __init__ returns the same value every time.

  • Don't copy the list: an array plus random.choice is fair, but uses O(N) extra memory and needs the length, which the follow-up takes away.

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd use Reservoir Sampling. I don't know the length and I don't want to copy the list, so I pick while I walk. I keep one candidate, result, and a counter i. At each node I add one to i, and I replace result with this node's value when a random number from zero to i minus one is zero, so with chance exactly one over i. That's fair: the new node gets in with one over i, and every earlier node, which held the spot with one over i minus one, survives with i minus one over i, which leaves it at one over i as well. The trap is the counter: it has to include this node before the draw, otherwise the head only gets a half and the answer can stay at zero. And I never stop early. That's O(N) time per call and O(1) extra space.

So: walk once per call, count the nodes, keep the i-th with chance 1/i, and never stop early.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N) per getRandom

__init__ only stores head: O(1). Each getRandom runs while node: once per node, N times, and each pass does one i += 1, one random.randrange(i) draw (O(1)), at most one assignment to result, and one step node = node.next. Nothing else loops, so a call costs O(N). A copy into an array would make each pick O(1) after an O(N) copy, but the win here is memory, not time.

SPACE COMPLEXITY

O(1)

The object keeps one reference, self.head, and getRandom keeps result, i and node: O(1) extra space, whatever the length. The list itself is the input and is never copied.

Formal Recurrence Relation

T(N) = N · (one increment + one draw + one step) = O(N) per call

__init__ only stores head: O(1). Each getRandom runs while node: once per node, N times, and each pass does one i += 1, one random.randrange(i) draw (O(1)), at most one assignment to result, and one step node = node.next. Nothing else loops, so a call costs O(N). A copy into an array would make each pick O(1) after an O(N) copy, but the win here is memory, not time.

Derivation Progression

Store the head

O(1)

__init__ keeps one reference and copies nothing.

One pass per call

N

while node: visits each node once and never stops early.

Work per node

O(1)

i += 1, one random.randrange(i) draw, at most one assignment, and node = node.next.

Total per getRandom

O(N)

N constant-time steps; k calls cost O(k · N).

Variable Definitions

NNN

Number of nodes in the list (at most 10^4)

Memory Architecture & Bounds

🟣 Call Stack

O(1): no recursion

🔵 Auxiliary Heap

O(1): self.head, result, i, node

🟢 Output Space

O(1): one integer per call

Boundary Best / Worst Cases

Best Case

O(N)O(N)O(N): every call walks the whole list

Average Case

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

Worst Case

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

Recurrence Tree Topology

Recurrence Tree Topology
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "every node has the same chance", "each call makes a new, independent choice", "its length is unknown". A uniform pick from a sequence read once with no room to store it: Reservoir Sampling, keep the i-th item with chance 1/i.

CONSTRAINTS & BOUNDS

Up to 10410^4104 nodes and 10410^4104 calls: one walk per call is up to 10810^8108 steps in the worst case, the price of O(1) extra space. With memory to spare, an array of the 10410^4104 values makes each pick O(1).

FAANG PRODUCTION TRAPS & EDGE CASES

The draw must be unbiased for every i: rand() % i in C carries a small bias when RAND_MAX + 1 is not a multiple of i, so production code uses an unbiased bounded generator. Sampling k items instead of one keeps a reservoir of k and replaces a random slot with chance k/i. On a stream split across machines, each worker keeps its own reservoir with its count, and the reservoirs are merged by picking from each in proportion to its count.

Core Algorithmic State Invariants

1. The i-th Item Gets Chance 1/i

`random.randrange(i) == 0` is true with chance exactly 1/i, and `i` already counts the current node, so the first node is always taken and every later one gets its fair share.

2. Every Earlier Item Stays at 1/i

An item that was `result` with chance 1/(i - 1) survives the i-th draw with (i - 1)/i, so it stays with 1/i: the shares stay equal at every step, and the walk must never stop early.

3. O(N) per Pick, O(1) Space

One walk per call with three variables: no copy of the list and no length needed, at the cost of O(N) time per `getRandom`.

Rosetta Dual-Monaco Comparison
Python 3
CANONICAL INVARIANT TEMPLATE
Loading...
CONCRETE: LINKED LIST RANDOM NODE (LEETCODE 382)
T = O(N) per getRandomS = O(1)
Loading...
Pattern Implementation Mapping TableCanonical Invariant ⟷ Concrete Code ⟷ Engineering Rationale
Canonical InvariantConcrete CodeEngineering Rationale
Keep only a pointer to the streamself.head = headNo copy of the values: every pick walks the list again, so the extra memory stays O(1).
One candidate and a counterresult, i = 0, 0 node = self.head`result` is the pick so far and `i` counts the nodes seen; the first node always replaces the starting 0.
Count the node before the drawi += 1`node` is now the i-th value, so its chance must be 1/i.
Keep it with chance exactly 1/i (the trap)if random.randrange(i) == 0: result = node.val`randrange(i)` is one of `0..i - 1`, so it is 0 with chance 1/i; every earlier node then keeps its share, 1/i.
Walk the whole stream, then answernode = node.next return resultNever stop at the first hit: the first node is always a hit. Only after the last node does every node have chance 1/n.
© 2026 Hi👋WebEnterprise. All rights reserved.
Sitemap•llms.txt•