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•Backtracking & State Search
MediumLC 78

Subsets (LeetCode 78)

Target Frequency:MetaAmazon

You are given an array nums of different integers. Return every subset of it: the empty subset, every subset in between, and the whole array. No subset may appear twice. You may return the subsets in any order.

Worked Examples

Example 1
Input:nums = [1,2,3]
Output:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
Explanation: Three numbers give 2^3 = 8 subsets. Each number is either in a subset or not.
Example 2
Input:nums = [0]
Output:[[],[0]]
Explanation: One number gives two subsets: the empty one and the one that holds 0.

⚖️Formal Constraints & Bounds

  • 1 <= nums.length <= 10

  • -10 <= nums[i] <= 10

  • All the numbers of nums are unique.

Deep-Dive & Conceptual Insights

Core Insight

Every node of the decision tree is a valid subset, so the code saves a copy of path at every call, and the loop picks only later indices so each subset has one order.

Real-World Scenario & Production Applications

Testing every on/off combination of a small set of feature flags is the same problem: each flag is either in the combination or out of it.

Step-by-Step Execution Trace Table

Code / Blueprint
backtrack(0) path = [] -> save []
pick 1 path = [1] -> save [1]
pick 2 path = [1, 2] -> save [1, 2]
pick 3 path = [1, 2, 3] -> save [1, 2, 3]
pick 3 path = [1, 3] -> save [1, 3]
pick 2 path = [2] -> save [2]
pick 3 path = [2, 3] -> save [2, 3]
pick 3 path = [3] -> save [3]
 
Step-by-Step Decision Trace
  1. Step 1 (first call): path = []. The call saves [] before it tries any choice.
  2. Step 2 (pick 1): path = [1]. The next call starts at index 1 and saves [1].
  3. Step 3 (pick 2, then 3): the calls save [1, 2] and [1, 2, 3]. After each branch, path.pop() removes the last number.
  4. Step 4 (back at [1], pick 3): path = [1, 3] is saved.
  5. Step 5 (back at the first call, pick 2, then 3): the calls save [2] and [2, 3].
  6. Step 6 (pick 3): [3] is saved.

Search complete: 8 subsets saved, one for each node of the tree.

Full Walkthrough6 Steps
Input:nums = [1,2,3]Expected:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
1⚡ STEP(first call)
path = []
path = []. The call saves [] before it tries any choice.
2⚡ STEP(pick 1)
path = [1]
path = [1]. The next call starts at index 1 and saves [1].
3↩️ BACKTRACK / POP(pick 2, then 3)
the calls save [1, 2] and [1, 2, 3]. After each branch, path.pop() removes the last number.
4⚡ STEP(back at `[1]`, pick 3)
path = [1, 3]
path = [1, 3] is saved.
5⚡ STEP(back at the first call, pick 2, then 3)
the calls save [2] and [2, 3].
6⚡ STEP(pick 3)
[3] is saved. Search complete: 8 subsets saved, one for each node of the tree.
Code Shape
Archetype Code Shape
Python
1results = [], path = []
2def backtrack(start):
3 results.append(list(path))
4 for i in range(start, len(nums)):
5 path.append(nums[i])
6 backtrack(i + 1)
7 path.pop()
8backtrack(0)
9return results

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Backtracking is a systematic method for exploring large, combinatorial decision spaces. Where simple iteration scans a flat list, backtracking explores a tree of potential choices where every node represents a decision frame. The foundational triad of the pattern is Choose, Explore, Un-Choose:

  1. Choose: Select a candidate element and add it to the active path.
  2. Explore: Recurse down the branch to evaluate all consequences of that choice.
  3. Un-Choose: Symmetrically remove the candidate from the path before returning to the parent, leaving zero trace of the experiment. For the power set (Subsets), we use a forward-advancing start index: each recursive step decides which remaining elements to include, naturally generating all 2N2^N2N unique subsets without generating duplicate permutations.
🏟️ The Analogy: Packing a Backpack for a Weekend Hike

Imagine packing a small backpack from a table of gear: a flashlight, a compass, and a camera. You start with an empty pack (Subset 1: []). You add the flashlight (Subset 2: ["flashlight"]). Keeping the flashlight in, you add the compass (Subset 3: ["flashlight", "compass"]). Keeping both, you add the camera (Subset 4: ["flashlight", "compass", "camera"]). You have reached the end of the table. You remove the camera (un-choose), remove the compass (un-choose), and try adding just the camera (Subset 5: ["flashlight", "camera"]). You unpack and repack one item at a time until every possible gear combination has been cataloged.

🔑 Why It Works

Notice results.append(path[:]). If you write results.append(path), Python stores a reference to the single mutable list. As backtracking unwinds, path.pop() empties that list, leaving results filled with empty arrays [[], [], ...]. Taking a snapshot path[:] freezes the state in amber.

  • Appending live path reference instead of shallow copy: results.append(path) stores a reference to the mutable list. When backtracking empties the list with path.pop(), all elements in results become empty lists []. Always append path[:] or list(path).

  • Duplicate subsets from unsorted inputs: If inputs contain duplicates, subsets will generate duplicate combinations unless inputs are sorted and sibling duplicates skipped.

  • Recursion stack depth exceeding limits: For N>20N > 20N>20, 2N2^N2N subsets exceed time and memory bounds. Ensure N≤20N \le 20N≤20.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

The statement asks for "all possible subsets (the power set)" of an array of different integers, and caps nums.length at 10. Every subset is wanted, including the empty one, so the answer is a list of lists and the input is small.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Invariant: path always holds exactly the choices from the root to the current node, and path is put back to its state before the call when backtrack returns. Every node is a valid subset, so backtrack saves a copy of path as soon as it is entered.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • Saving path and not list(path): path is one list that append and pop keep changing, so every saved subset ends up empty.

  • Calling backtrack(start + 1) and not backtrack(i + 1): the next call can pick the same index again, so a value appears twice in a subset.

  • Forgetting path.pop(): path keeps the old choice, and every later subset holds a wrong item.

The 60-Second Interview Pitch

Say this out loud before you type a single line

"I'd use Subsets. Every node of the decision tree is a valid subset, so backtrack(start) saves a copy of path as soon as it is entered, including the empty one at the first call. The loop picks only indices from start onward and recurses with i + 1, so each subset is built in one order and saved once. After each branch I pop the choice, so path is the same as before it. There are 2^N nodes and each copies path in up to O(N), so the time is O(N * 2^N) and the stack is O(N) deep."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(2^N · N)

There is one call of backtrack for each subset, so 2^N calls. Each call copies path into results, which costs up to N.

SPACE COMPLEXITY

O(N) Call Stack

The recursion stack is at most N calls deep, and path holds at most N numbers. The saved subsets are the output and are not counted.

Formal Recurrence Relation

T(N) = 2^N · O(N) = O(2^N · N)

Derivation Progression

One Call per Subset

2^N calls

Each call of backtrack saves one subset and loops over the later indices, so there is exactly one call for each of the 2^N subsets.

Copy per Call

up to N values

Each call copies path into results, which costs up to N values.

Variable Definitions

NNN

Number of elements in the input array

Memory Architecture & Bounds

🟣 Call Stack

O(N) Call stack depth

🔵 Auxiliary Heap

O(N) Current subset path buffer

🟢 Output Space

O(2^N · N) Array of all subsets

Boundary Best / Worst Cases

Best Case

O(2N⋅N)O(2^N · N)O(2N⋅N): every input of N different numbers has the same 2^N subsets

Average Case

O(2N⋅N)O(2^N · N)O(2N⋅N)

Worst Case

O(2N⋅N)O(2^N · N)O(2N⋅N)

Decision & State-Space Search Tree

Decision & State-Space Search Tree
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

In-Place State Mutation vs Heap Object Allocation

Senior SWEs mutate shared accumulator state in-place (e.g. path.append(x) followed by path.pop()) rather than cloning arrays at each recursive node. Cloning arrays at depth D creates O(2^D) temporary heap objects, destroying CPU cache and triggering massive Garbage Collection pauses.

path.append(x); backtrack(); path.pop() // Zero allocation: O(1) state restoration
Stack Depth Boundedness & Minimal Memory Footprint

Because backtracking recursion depth is strictly bounded by N (e.g. N <= 20 for combinations), the call stack consumes negligible memory (< a few kilobytes), fitting entirely inside L1 CPU instruction and data caches.

Theory Context•Backtracking & State Search
MediumLC 78

Subsets (LeetCode 78)

Target Frequency:MetaAmazon

You are given an array nums of different integers. Return every subset of it: the empty subset, every subset in between, and the whole array. No subset may appear twice. You may return the subsets in any order.

Worked Examples

Example 1
Input:nums = [1,2,3]
Output:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
Explanation: Three numbers give 2^3 = 8 subsets. Each number is either in a subset or not.
Example 2
Input:nums = [0]
Output:[[],[0]]
Explanation: One number gives two subsets: the empty one and the one that holds 0.

⚖️Formal Constraints & Bounds

  • 1 <= nums.length <= 10

  • -10 <= nums[i] <= 10

  • All the numbers of nums are unique.

Deep-Dive & Conceptual Insights

Core Insight

Every node of the decision tree is a valid subset, so the code saves a copy of path at every call, and the loop picks only later indices so each subset has one order.

Real-World Scenario & Production Applications

Testing every on/off combination of a small set of feature flags is the same problem: each flag is either in the combination or out of it.

Step-by-Step Execution Trace Table

Code / Blueprint
backtrack(0) path = [] -> save []
pick 1 path = [1] -> save [1]
pick 2 path = [1, 2] -> save [1, 2]
pick 3 path = [1, 2, 3] -> save [1, 2, 3]
pick 3 path = [1, 3] -> save [1, 3]
pick 2 path = [2] -> save [2]
pick 3 path = [2, 3] -> save [2, 3]
pick 3 path = [3] -> save [3]
 
Step-by-Step Decision Trace
  1. Step 1 (first call): path = []. The call saves [] before it tries any choice.
  2. Step 2 (pick 1): path = [1]. The next call starts at index 1 and saves [1].
  3. Step 3 (pick 2, then 3): the calls save [1, 2] and [1, 2, 3]. After each branch, path.pop() removes the last number.
  4. Step 4 (back at [1], pick 3): path = [1, 3] is saved.
  5. Step 5 (back at the first call, pick 2, then 3): the calls save [2] and [2, 3].
  6. Step 6 (pick 3): [3] is saved.

Search complete: 8 subsets saved, one for each node of the tree.

Full Walkthrough6 Steps
Input:nums = [1,2,3]Expected:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
1⚡ STEP(first call)
path = []
path = []. The call saves [] before it tries any choice.
2⚡ STEP(pick 1)
path = [1]
path = [1]. The next call starts at index 1 and saves [1].
3↩️ BACKTRACK / POP(pick 2, then 3)
the calls save [1, 2] and [1, 2, 3]. After each branch, path.pop() removes the last number.
4⚡ STEP(back at `[1]`, pick 3)
path = [1, 3]
path = [1, 3] is saved.
5⚡ STEP(back at the first call, pick 2, then 3)
the calls save [2] and [2, 3].
6⚡ STEP(pick 3)
[3] is saved. Search complete: 8 subsets saved, one for each node of the tree.
Code Shape
Archetype Code Shape
Python
1results = [], path = []
2def backtrack(start):
3 results.append(list(path))
4 for i in range(start, len(nums)):
5 path.append(nums[i])
6 backtrack(i + 1)
7 path.pop()
8backtrack(0)
9return results

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Backtracking is a systematic method for exploring large, combinatorial decision spaces. Where simple iteration scans a flat list, backtracking explores a tree of potential choices where every node represents a decision frame. The foundational triad of the pattern is Choose, Explore, Un-Choose:

  1. Choose: Select a candidate element and add it to the active path.
  2. Explore: Recurse down the branch to evaluate all consequences of that choice.
  3. Un-Choose: Symmetrically remove the candidate from the path before returning to the parent, leaving zero trace of the experiment. For the power set (Subsets), we use a forward-advancing start index: each recursive step decides which remaining elements to include, naturally generating all 2N2^N2N unique subsets without generating duplicate permutations.
🏟️ The Analogy: Packing a Backpack for a Weekend Hike

Imagine packing a small backpack from a table of gear: a flashlight, a compass, and a camera. You start with an empty pack (Subset 1: []). You add the flashlight (Subset 2: ["flashlight"]). Keeping the flashlight in, you add the compass (Subset 3: ["flashlight", "compass"]). Keeping both, you add the camera (Subset 4: ["flashlight", "compass", "camera"]). You have reached the end of the table. You remove the camera (un-choose), remove the compass (un-choose), and try adding just the camera (Subset 5: ["flashlight", "camera"]). You unpack and repack one item at a time until every possible gear combination has been cataloged.

🔑 Why It Works

Notice results.append(path[:]). If you write results.append(path), Python stores a reference to the single mutable list. As backtracking unwinds, path.pop() empties that list, leaving results filled with empty arrays [[], [], ...]. Taking a snapshot path[:] freezes the state in amber.

  • Appending live path reference instead of shallow copy: results.append(path) stores a reference to the mutable list. When backtracking empties the list with path.pop(), all elements in results become empty lists []. Always append path[:] or list(path).

  • Duplicate subsets from unsorted inputs: If inputs contain duplicates, subsets will generate duplicate combinations unless inputs are sorted and sibling duplicates skipped.

  • Recursion stack depth exceeding limits: For N>20N > 20N>20, 2N2^N2N subsets exceed time and memory bounds. Ensure N≤20N \le 20N≤20.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

The statement asks for "all possible subsets (the power set)" of an array of different integers, and caps nums.length at 10. Every subset is wanted, including the empty one, so the answer is a list of lists and the input is small.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Invariant: path always holds exactly the choices from the root to the current node, and path is put back to its state before the call when backtrack returns. Every node is a valid subset, so backtrack saves a copy of path as soon as it is entered.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • Saving path and not list(path): path is one list that append and pop keep changing, so every saved subset ends up empty.

  • Calling backtrack(start + 1) and not backtrack(i + 1): the next call can pick the same index again, so a value appears twice in a subset.

  • Forgetting path.pop(): path keeps the old choice, and every later subset holds a wrong item.

The 60-Second Interview Pitch

Say this out loud before you type a single line

"I'd use Subsets. Every node of the decision tree is a valid subset, so backtrack(start) saves a copy of path as soon as it is entered, including the empty one at the first call. The loop picks only indices from start onward and recurses with i + 1, so each subset is built in one order and saved once. After each branch I pop the choice, so path is the same as before it. There are 2^N nodes and each copies path in up to O(N), so the time is O(N * 2^N) and the stack is O(N) deep."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(2^N · N)

There is one call of backtrack for each subset, so 2^N calls. Each call copies path into results, which costs up to N.

SPACE COMPLEXITY

O(N) Call Stack

The recursion stack is at most N calls deep, and path holds at most N numbers. The saved subsets are the output and are not counted.

Formal Recurrence Relation

T(N) = 2^N · O(N) = O(2^N · N)

Derivation Progression

One Call per Subset

2^N calls

Each call of backtrack saves one subset and loops over the later indices, so there is exactly one call for each of the 2^N subsets.

Copy per Call

up to N values

Each call copies path into results, which costs up to N values.

Variable Definitions

NNN

Number of elements in the input array

Memory Architecture & Bounds

🟣 Call Stack

O(N) Call stack depth

🔵 Auxiliary Heap

O(N) Current subset path buffer

🟢 Output Space

O(2^N · N) Array of all subsets

Boundary Best / Worst Cases

Best Case

O(2N⋅N)O(2^N · N)O(2N⋅N): every input of N different numbers has the same 2^N subsets

Average Case

O(2N⋅N)O(2^N · N)O(2N⋅N)

Worst Case

O(2N⋅N)O(2^N · N)O(2N⋅N)

Decision & State-Space Search Tree

Decision & State-Space Search Tree
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

In-Place State Mutation vs Heap Object Allocation

Senior SWEs mutate shared accumulator state in-place (e.g. path.append(x) followed by path.pop()) rather than cloning arrays at each recursive node. Cloning arrays at depth D creates O(2^D) temporary heap objects, destroying CPU cache and triggering massive Garbage Collection pauses.

path.append(x); backtrack(); path.pop() // Zero allocation: O(1) state restoration
Stack Depth Boundedness & Minimal Memory Footprint

Because backtracking recursion depth is strictly bounded by N (e.g. N <= 20 for combinations), the call stack consumes negligible memory (< a few kilobytes), fitting entirely inside L1 CPU instruction and data caches.

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