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•Advanced Data Structures
MediumLC 208

Implement Trie (Prefix Tree) (LeetCode 208)

Target Frequency:AmazonMetaNetflix

A trie ("try") or prefix tree is a tree structure purpose-built for storing and looking up a set of strings efficiently, and it underlies things like autocomplete and spellcheckers.

Implement a Trie class:

  • Trie() starts with an empty trie.
  • void insert(String word) adds word to the trie.
  • boolean search(String word) returns true only if word itself was previously inserted.
  • boolean startsWith(String prefix) returns true if any inserted word begins with prefix.

Worked Examples

Example 1
Input:["Trie", "insert", "search", "search", "startsWith", "insert", "search"] [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
Output:[null, null, true, false, true, null, true]
Explanation: After inserting "apple", search("apple") is true, search("app") is false (only a prefix so far), and startsWith("app") is true. After inserting "app", search("app") is true.

⚖️Formal Constraints & Bounds

  • 1 <= word.length, prefix.length <= 2000

  • word and prefix consist only of lowercase English letters.

  • At most 3 * 104 calls in total will be made to insert, search, and startsWith.

Deep-Dive & Conceptual Insights

Core Insight

Each tree node has 26 possible children and an is_end_of_word boolean flag. Lookup time is strictly proportional to word length L, independent of dictionary size N.

Real-World Scenario & Production Applications

Search engine autocomplete (e.g. Google search query prefix recommendations) and IP routing prefix lookup tables (CIDR) are powered by Tries.

Step-by-Step Execution Trace Table

StepInvocationPath WalkedNode Landed Onis_end CheckedOutput ValuePedagogical Reason
1insert("apple")Creates a → p → p → l → ee nodeSet to True—Allocates 5 nodes; marks 'e' as end.
2search("apple")Traverses a → p → p → l → ee nodeTrueTruePath exists AND is_end == True.
3search("app")Traverses a → p → pSecond p nodeFalseFalsePath exists BUT is_end == False.
4startsWith("app")Traverses a → p → pSecond p nodeNot checkedTruePath exists (prefix matches!).
5insert("app")Walks existing a → p → pSecond p nodeSet to True—Zero new nodes allocated; flips is_end.
6search("app")Traverses a → p → pSecond p nodeTrueTruePath exists AND is_end == True.
Code Shape
Archetype Code Shape
Python
1each TrieNode holds children = {} and is_end = False
2def _walk(s):
3 follow node.children[ch] for each ch, returning None at a missing child
4 return the last node
5def insert(word):
6 node = node.children.setdefault(ch, TrieNode()) for each ch
7 node.is_end = True on the last node
8def search(word):
9 node = self._walk(word); return node is not None and node.is_end
10def startsWith(prefix):
11 return self._walk(prefix) is not None

Conceptual Narrative

Storing "cat", "cats", and "category" as independent strings triplicates the common prefix "cat". A Trie collapses identical prefixes into a single shared path, turning prefix queries into linear descents.

🧭 Conceptual Foundation & Pattern Intuition

A hash set can verify if a string exists in O(L)O(L)O(L) average time, but answering startsWith(prefix) requires either hashing all O(L2)O(L^2)O(L2) possible substrings of every word or scanning the entire dictionary in O(N⋅L)O(N \cdot L)O(N⋅L) time. A Trie (from re-TRIE-val) structures strings as a 26-ary tree where:

  • Each node represents a single character transition.
  • The root represents the empty string "".
  • All words sharing a common prefix of length LLL traverse the exact same sequence of LLL nodes.
  • An is_end boolean flag distinguishes complete stored words from intermediate prefixes. Because descent depends solely on the characters of the query string, all operations run in strict O(L)O(L)O(L) time, regardless of whether the dictionary contains 10 words or 10,000,000 words.
🏛️ The Analogy: Walking a Numbered Library Hallway

Imagine entering an archive where every corridor branches into 26 numbered doors labeled 'a' through 'z'. To find the book "apple", you walk through door 'a', then down that hallway to door 'p', then 'p', 'l', and 'e'. At room 'e', a green bookmark rests on the table (is_end = True), confirming "apple" is a complete cataloged book. If someone asks if any book starts with "app", you only walk to door 'p' and verify the hallway continues—you never need to inspect the individual titles shelved further down the hall.

🔑 Why It Works

The elegance is that search and startsWith share 100% of their tree-walk mechanics through _walk; search merely requires node.is_end at the termination node, whereas startsWith returns True as long as the path does not hit a dead end.

  • Conflating path existence with word completion: search must verify node.is_end == True. Checking only that the character chain exists causes prefix-only matches to incorrectly return True.

  • Mutating state during search via defaultdict: Using defaultdict or setdefault inside search() or startsWith() silently creates empty nodes for non-existent characters, corrupting prefix lookups on subsequent queries.

  • Premature is_end flag assignment: Setting is_end = True on the parent node before traversing to the child corresponding to the final character marks an incomplete word as stored.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

Word dictionary + autocomplete prefix searching.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Invariant: every root-to-node path represents a unique character prefix.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • search returns node is not None and node.is_end: without the is_end check, search("app") is true after inserting "apple".

  • Using Python defaultdict for children in search: Accessing node.children[ch] with a defaultdict silently inserts empty nodes during a read-only search or startsWith query, corrupting trie structure and bloating memory.

  • Memory overhead for sparse tries: Storing fixed-size 26-element arrays for unicode or sparse alphabets wastes space; use a hash table children = {} or radix tree when alphabet size is large or sparse.

The 60-Second Interview Pitch

Say this out loud before you type a single line

"I'd use a Trie. Each node has a dict of children and an is_end flag. insert walks the word, creates missing children, then sets is_end on the last node. search and startsWith share one walk that stops as soon as a child is missing, so reads never add nodes. This is correct because a path exists exactly when some inserted word has that prefix. The trap is that search must also check is_end, or search("app") is true after inserting "apple"; startsWith skips that check, because any path at all proves that some word begins with the prefix. Each call is O(L) time, L = word length, and the trie takes one node per distinct prefix, O(total characters) space."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(L) per operation, L = word length

Look at the code: insert loops over the word once, and search and startsWith each call _walk, which loops over the word or prefix once. Every loop step is one dict lookup or one setdefault, O(1). So each call is O(L), however many words are stored.

SPACE COMPLEXITY

O(total characters)

Look at memory allocations: insert and _walk are loops, so the call stack is O(1). The trie holds one TrieNode per distinct prefix of the inserted words, and each node's children dict holds only the edges that exist. One insert adds at most L new nodes, so all nodes together number at most the total characters of the inserted words: O(total characters).

Formal Recurrence Relation

T(L) = L transitions = O(L)

Derivation Progression

Root-to-Leaf Traversal

node = node.children[ch]

Follows one child per character, an O(1) dict lookup.

Prefix Sharing

node.children.setdefault(ch, TrieNode())

insert creates a node only when the child is missing, so words with a common prefix reuse its nodes.

Word-Length Bound

O(L) where L = len(word)

Operations are independent of total dictionary size N.

Variable Definitions

LLL

Length of the word or prefix passed to one call

NNN

Total number of words stored in Trie

totalcharacterstotal characterstotalcharacters

Sum of the lengths of all inserted words

Memory Architecture & Bounds

🟣 Call Stack

O(1) Iterative

🔵 Auxiliary Heap

O(total characters): one TrieNode per distinct prefix

🟢 Output Space

O(1) Boolean return

Boundary Best / Worst Cases

Best Case

O(1)O(1)O(1) on early mismatch in search.

Average Case

O(L)O(L)O(L).

Worst Case

O(L)O(L)O(L) where L is word length.

Recurrence Tree Topology

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

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "prefix tree", "startsWith(prefix)", "search(word)". Hash sets cannot answer prefix membership without inspecting every candidate string. A Senior SWE immediately applies a Trie (Prefix Tree): an alphabet-branching tree where all strings sharing a common prefix traverse the exact same path from the root.

CONSTRAINTS & BOUNDS

Words consist of lowercase English letters (a−za-za−z), length L≤2000L \le 2000L≤2000, up to 3×1043 \times 10^43×104 total operations. Time budget is strictly O(L)O(L)O(L) per insert, search, and prefix query, independent of the number of words stored (NNN).

FAANG PRODUCTION TRAPS & EDGE CASES

Deep paths: a single word of length 2000 builds a 2000-node chain, so a recursive walk or delete can hit Python's default recursion limit; keep insert and _walk as loops. Memory per node: every node is an object plus a dict, which costs far more than the one character it stands for, so a large dictionary may need a fixed child array or a compressed (radix) trie that stores a run of single-child nodes as one edge.

Theory Context•Advanced Data Structures
MediumLC 208

Implement Trie (Prefix Tree) (LeetCode 208)

Target Frequency:AmazonMetaNetflix

A trie ("try") or prefix tree is a tree structure purpose-built for storing and looking up a set of strings efficiently, and it underlies things like autocomplete and spellcheckers.

Implement a Trie class:

  • Trie() starts with an empty trie.
  • void insert(String word) adds word to the trie.
  • boolean search(String word) returns true only if word itself was previously inserted.
  • boolean startsWith(String prefix) returns true if any inserted word begins with prefix.

Worked Examples

Example 1
Input:["Trie", "insert", "search", "search", "startsWith", "insert", "search"] [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
Output:[null, null, true, false, true, null, true]
Explanation: After inserting "apple", search("apple") is true, search("app") is false (only a prefix so far), and startsWith("app") is true. After inserting "app", search("app") is true.

⚖️Formal Constraints & Bounds

  • 1 <= word.length, prefix.length <= 2000

  • word and prefix consist only of lowercase English letters.

  • At most 3 * 104 calls in total will be made to insert, search, and startsWith.

Deep-Dive & Conceptual Insights

Core Insight

Each tree node has 26 possible children and an is_end_of_word boolean flag. Lookup time is strictly proportional to word length L, independent of dictionary size N.

Real-World Scenario & Production Applications

Search engine autocomplete (e.g. Google search query prefix recommendations) and IP routing prefix lookup tables (CIDR) are powered by Tries.

Step-by-Step Execution Trace Table

StepInvocationPath WalkedNode Landed Onis_end CheckedOutput ValuePedagogical Reason
1insert("apple")Creates a → p → p → l → ee nodeSet to True—Allocates 5 nodes; marks 'e' as end.
2search("apple")Traverses a → p → p → l → ee nodeTrueTruePath exists AND is_end == True.
3search("app")Traverses a → p → pSecond p nodeFalseFalsePath exists BUT is_end == False.
4startsWith("app")Traverses a → p → pSecond p nodeNot checkedTruePath exists (prefix matches!).
5insert("app")Walks existing a → p → pSecond p nodeSet to True—Zero new nodes allocated; flips is_end.
6search("app")Traverses a → p → pSecond p nodeTrueTruePath exists AND is_end == True.
Code Shape
Archetype Code Shape
Python
1each TrieNode holds children = {} and is_end = False
2def _walk(s):
3 follow node.children[ch] for each ch, returning None at a missing child
4 return the last node
5def insert(word):
6 node = node.children.setdefault(ch, TrieNode()) for each ch
7 node.is_end = True on the last node
8def search(word):
9 node = self._walk(word); return node is not None and node.is_end
10def startsWith(prefix):
11 return self._walk(prefix) is not None

Conceptual Narrative

Storing "cat", "cats", and "category" as independent strings triplicates the common prefix "cat". A Trie collapses identical prefixes into a single shared path, turning prefix queries into linear descents.

🧭 Conceptual Foundation & Pattern Intuition

A hash set can verify if a string exists in O(L)O(L)O(L) average time, but answering startsWith(prefix) requires either hashing all O(L2)O(L^2)O(L2) possible substrings of every word or scanning the entire dictionary in O(N⋅L)O(N \cdot L)O(N⋅L) time. A Trie (from re-TRIE-val) structures strings as a 26-ary tree where:

  • Each node represents a single character transition.
  • The root represents the empty string "".
  • All words sharing a common prefix of length LLL traverse the exact same sequence of LLL nodes.
  • An is_end boolean flag distinguishes complete stored words from intermediate prefixes. Because descent depends solely on the characters of the query string, all operations run in strict O(L)O(L)O(L) time, regardless of whether the dictionary contains 10 words or 10,000,000 words.
🏛️ The Analogy: Walking a Numbered Library Hallway

Imagine entering an archive where every corridor branches into 26 numbered doors labeled 'a' through 'z'. To find the book "apple", you walk through door 'a', then down that hallway to door 'p', then 'p', 'l', and 'e'. At room 'e', a green bookmark rests on the table (is_end = True), confirming "apple" is a complete cataloged book. If someone asks if any book starts with "app", you only walk to door 'p' and verify the hallway continues—you never need to inspect the individual titles shelved further down the hall.

🔑 Why It Works

The elegance is that search and startsWith share 100% of their tree-walk mechanics through _walk; search merely requires node.is_end at the termination node, whereas startsWith returns True as long as the path does not hit a dead end.

  • Conflating path existence with word completion: search must verify node.is_end == True. Checking only that the character chain exists causes prefix-only matches to incorrectly return True.

  • Mutating state during search via defaultdict: Using defaultdict or setdefault inside search() or startsWith() silently creates empty nodes for non-existent characters, corrupting prefix lookups on subsequent queries.

  • Premature is_end flag assignment: Setting is_end = True on the parent node before traversing to the child corresponding to the final character marks an incomplete word as stored.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

Word dictionary + autocomplete prefix searching.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Invariant: every root-to-node path represents a unique character prefix.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • search returns node is not None and node.is_end: without the is_end check, search("app") is true after inserting "apple".

  • Using Python defaultdict for children in search: Accessing node.children[ch] with a defaultdict silently inserts empty nodes during a read-only search or startsWith query, corrupting trie structure and bloating memory.

  • Memory overhead for sparse tries: Storing fixed-size 26-element arrays for unicode or sparse alphabets wastes space; use a hash table children = {} or radix tree when alphabet size is large or sparse.

The 60-Second Interview Pitch

Say this out loud before you type a single line

"I'd use a Trie. Each node has a dict of children and an is_end flag. insert walks the word, creates missing children, then sets is_end on the last node. search and startsWith share one walk that stops as soon as a child is missing, so reads never add nodes. This is correct because a path exists exactly when some inserted word has that prefix. The trap is that search must also check is_end, or search("app") is true after inserting "apple"; startsWith skips that check, because any path at all proves that some word begins with the prefix. Each call is O(L) time, L = word length, and the trie takes one node per distinct prefix, O(total characters) space."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(L) per operation, L = word length

Look at the code: insert loops over the word once, and search and startsWith each call _walk, which loops over the word or prefix once. Every loop step is one dict lookup or one setdefault, O(1). So each call is O(L), however many words are stored.

SPACE COMPLEXITY

O(total characters)

Look at memory allocations: insert and _walk are loops, so the call stack is O(1). The trie holds one TrieNode per distinct prefix of the inserted words, and each node's children dict holds only the edges that exist. One insert adds at most L new nodes, so all nodes together number at most the total characters of the inserted words: O(total characters).

Formal Recurrence Relation

T(L) = L transitions = O(L)

Derivation Progression

Root-to-Leaf Traversal

node = node.children[ch]

Follows one child per character, an O(1) dict lookup.

Prefix Sharing

node.children.setdefault(ch, TrieNode())

insert creates a node only when the child is missing, so words with a common prefix reuse its nodes.

Word-Length Bound

O(L) where L = len(word)

Operations are independent of total dictionary size N.

Variable Definitions

LLL

Length of the word or prefix passed to one call

NNN

Total number of words stored in Trie

totalcharacterstotal characterstotalcharacters

Sum of the lengths of all inserted words

Memory Architecture & Bounds

🟣 Call Stack

O(1) Iterative

🔵 Auxiliary Heap

O(total characters): one TrieNode per distinct prefix

🟢 Output Space

O(1) Boolean return

Boundary Best / Worst Cases

Best Case

O(1)O(1)O(1) on early mismatch in search.

Average Case

O(L)O(L)O(L).

Worst Case

O(L)O(L)O(L) where L is word length.

Recurrence Tree Topology

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

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "prefix tree", "startsWith(prefix)", "search(word)". Hash sets cannot answer prefix membership without inspecting every candidate string. A Senior SWE immediately applies a Trie (Prefix Tree): an alphabet-branching tree where all strings sharing a common prefix traverse the exact same path from the root.

CONSTRAINTS & BOUNDS

Words consist of lowercase English letters (a−za-za−z), length L≤2000L \le 2000L≤2000, up to 3×1043 \times 10^43×104 total operations. Time budget is strictly O(L)O(L)O(L) per insert, search, and prefix query, independent of the number of words stored (NNN).

FAANG PRODUCTION TRAPS & EDGE CASES

Deep paths: a single word of length 2000 builds a 2000-node chain, so a recursive walk or delete can hit Python's default recursion limit; keep insert and _walk as loops. Memory per node: every node is an object plus a dict, which costs far more than the one character it stands for, so a large dictionary may need a fixed child array or a compressed (radix) trie that stores a run of single-child nodes as one edge.

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