Course Roadmap: What We'll Build & Learn
A complete, zero-fluff cheat sheet covering the 9 core data structures essential for coding interviews. Master contiguous memory, LIFO/FIFO access, dynamic pointer rewiring, hash collision mechanics, BST invariants, heap extrema, graph topological traversals, prefix tries, Big-O complexities, and keyword pattern matching.
What You Need Before Starting:
- Basic programming proficiency in any major language (TypeScript/JavaScript, Python, C++, or Java)
- Familiarity with loops, arrays, conditional statements, and recursive function calls
- No advanced competitive programming required — all concepts are explained from fundamental memory models
The 9 Core Data Structures: The Complete Interview Decision Architecture
Master memory models, operational Big-O complexities, and keyword triggers to solve 95% of coding interview problems.
In technical coding interviews and real-world software architecture alike, selecting the optimal data structure is often 80% of solving the problem. Every data structure is an engineering trade-off balancing memory layout, read speeds, insertion overhead, and search constraints.
Rather than attempting to memorize hundreds of disconnected LeetCode problems, successful engineers use a structured mental taxonomy of 9 foundational data structures: Arrays, Stacks, Queues, Linked Lists, Hash Maps, Trees, Heaps, Graphs, and Tries.
This masterclass breaks down all 9 core structures from memory allocation fundamentals to Big-O operational tables, problem-statement keyword triggers, and canonical interview problem sets.
Traditional Methods vs. Data Structures & Algorithms
Memory Allocation Model
Element Access Mechanism
Interview Problem Strategy
The 6 Core Superpowers We Will Master
Contiguous Arrays & Memory Offsets
Leverage direct base-pointer arithmetic for O(1) instant random access and CPU cache locality.
LIFO Stacks & Execution Unwinding
Enforce strict single-end push/pop access discipline for bracket matching, backtracking, and DFS call stacks.
Linked Lists & Pointer Rewiring
Perform O(1) dynamic insertions and deletions without memory shifts using singly and doubly linked nodes.
O(1) Hash Map Key-Value Lookups
Transform quadratic brute-force searches into constant-time queries via hash functions and bucket arrays.
Hierarchical Binary Search Trees
Maintain sorted data with O(log N) search and in-order traversals without full array reallocation.
Heaps, Graphs & Prefix Tries
Extract streaming extrema in O(1), traverse complex network topologies, and search string prefixes in O(M) time.
Quick Cheat Sheet: Essential Commands
Keep these core syntax triggers handy as you follow the walkthrough modules below:
| Command / Trigger | What It Does |
|---|---|
Array: arr[i] | O(1) random access via memory address math (base + i * size). Middle insert/delete: O(n). |
Stack: push / pop / top | LIFO order. O(1) top access. Triggered by undo, brackets, DFS, monotonic patterns. |
Queue: enqueue / dequeue | FIFO order. O(1) push rear / pop front. Triggered by BFS, level-order, task scheduling. |
Linked List: head -> next | O(1) insert/delete with known node reference. O(n) indexed lookup. Dynamic heap memory. |
Hash Map: map.get(key) | O(1) avg lookup/insert. Unordered keys. Triggered by frequency, seen sets, anagrams. |
BST: left < root < right | O(log n) balanced search/insert. Degrades to O(n) if skewed. In-order traversal yields sorted sequence. |
Heap: peek min/max | O(1) extrema access, O(log n) insert/extract. Triggered by 'Top K', 'Kth largest', 'Median in stream'. |
Graph: BFS / DFS + visited | O(V + E) traversal. Always track visited nodes to avoid infinite cycles in cyclic topologies. |
Trie: root -> char branches | O(m) prefix search where m is word length. Completely independent of total dictionary size. |
Array Mechanics & Contiguous Memory Architecture
Understand how contiguous memory allocation powers O(1) index jumping, why middle modifications take O(n), and how to recognize array pattern keywords in interviews.
An array is the fundamental contiguous data structure in computer science. When an array of size 4 integers is allocated, the system reserves a contiguous 16-byte block in memory (4 elements × 4 bytes). Index 0 marks the base memory address (e.g., `0x5BC0`). To access index `i`, the CPU computes `Address(i) = BaseAddress + (i × SizeOfElement)` in a single hardware cycle, providing instant O(1) random access. However, inserting or deleting in the middle requires shifting up to N elements, incurring an O(n) time penalty.
1Step 1: Compute Contiguous Memory Address Offsets
$ Base Address = 0x5BC0, Element Size = 4 bytes (int). Target: Index 32Step 2: Understand Element Shifting Overhead on Middle Modification
$ Delete element at index 1 from array [10, 20, 30, 40]Two-pointer technique on sorted arrays achieving O(n) time complexity and O(1) space complexity by exploiting contiguous indexed boundaries.
// Master Array Complexities & In-Place Two-Pointer Pattern
// Access by Index: O(1)
// Search (Unsorted): O(n)
// Insert / Delete at End: O(1) amortized
// Insert / Delete in Middle: O(n) due to element shifting
export function twoSumTwoPointersSorted(numbers: number[], target: number): number[] {
let left = 0;
let right = numbers.length - 1;
while (left < right) {
const currentSum = numbers[left] + numbers[right];
if (currentSum === target) {
return [left, right];
} else if (currentSum < target) {
left++; // Increase sum by moving left pointer forward
} else {
right--; // Decrease sum by moving right pointer backward
}
}
return [];
}Do:Leverage arrays when random index access and cache locality are critical
Arrays store elements sequentially in RAM, allowing the CPU cache line prefetcher to load adjacent items with minimal cache misses.
Avoid:Frequent middle insertions and deletions in large arrays
If an algorithm requires continuous insertions in the middle of a collection, prefer a Doubly Linked List or Balanced Tree to avoid O(n) shifts.
Pro Tip for Beginners
Common Beginner Pitfall & How to Fix It
What happens: Off-by-one errors and array boundary index out-of-bounds
How to solve it: Always verify loop boundaries (`< length` vs `<= length - 1`) and ensure empty/single-element arrays are handled as base cases.
Hands-On Practice Checklist
Try each of these steps on your computer and check them off as you complete them:
Stack (LIFO) & Function Call Stack Mechanics
Master Last-In, First-Out (LIFO) access discipline, call stack unwinding, monotonic stacks, and balanced bracket evaluation.
A Stack is a strict Last-In, First-Out (LIFO) container where insertions (`push`) and removals (`pop`) occur exclusively at a single designated end: the `top`. You can think of it like a stack of plates—you always place new plates on top and remove the top plate first. This constraint makes the stack ideal for tracking history, reversing sequences, parsing nested grammar, and unwinding recursion call frames. In an interview, when implementing a stack with an underlying array, you must enforce this discipline and never access or modify middle elements directly.
1Step 1: Push Operations Onto the Stack
$ Push sequence: 'A' → 'B' → 'C' → 'D'2Step 2: Pop & Inspect Extrema
$ Pop from StackCanonical Valid Parentheses algorithm matching nested brackets in O(n) time and O(n) space using a LIFO stack.
// Stack Complexities:
// Push: O(1) | Pop: O(1) | Top / Peek: O(1) | Size: O(1) | Search: O(n)
export function isValidParentheses(s: string): boolean {
const stack: string[] = [];
const bracketMap: Record<string, string> = {
')': '(',
'}': '{',
']': '['
};
for (const char of s) {
if (char === '(' || char === '{' || char === '[') {
stack.push(char);
} else if (char in bracketMap) {
if (stack.length === 0 || stack.pop() !== bracketMap[char]) {
return false;
}
}
}
return stack.length === 0;
}Do:Respect the LIFO single-end access discipline
Even when using a dynamic array or list to implement a stack, only invoke push, pop, and top operations to preserve stack correctness.
Avoid:Attempting random middle indexing in a stack
If an algorithm requires peeking at the 3rd or 4th element down without popping preceding items, a stack is the wrong data structure.
Pro Tip for Beginners
Common Beginner Pitfall & How to Fix It
What happens: Stack Underflow (popping an empty stack)
How to solve it: Always verify `stack.length > 0` before invoking `pop()` or inspecting the `top` element.
Hands-On Practice Checklist
Try each of these steps on your computer and check them off as you complete them:
Linked Lists (Singly & Doubly) & Dynamic Memory Rewiring
Master node-pointer chaining in heap memory, singly vs doubly linked lists, O(1) rewiring, and Floyd's cycle detection.
A Linked List is a linear sequence of node structures where each node holds a data value and one or more pointer references to adjacent nodes (`next` for Singly Linked Lists, and both `next` and `prev` for Doubly Linked Lists). Unlike arrays, linked list nodes are allocated dynamically across heap memory (e.g., node A at `0x5B10`, node B at `0xC012`). Inserting or deleting a node requires only updating adjacent pointers (O(1) rewiring if you hold the reference), without shifting remaining elements. However, accessing an element by index requires walking sequentially from the head (O(n) time), and scattered memory allocations are not CPU cache friendly.
1Step 1: Inspect Singly Linked List Node Memory Layout
$ Node A [Val: 10 | Next: 0xC012] -> Node B [Val: 20 | Next: 0x8F40] -> Node C [Val: 30 | Next: null]2Step 2: O(1) Node Insertion via Pointer Rewiring
$ Insert Node X [Val: 15] between Node A and Node BSingly Linked List node definition alongside Floyd's Fast & Slow Pointers cycle detection algorithm in O(n) time and O(1) space.
// Linked List Complexities:
// Access / Search by Index: O(n)
// Insert / Delete (with known pointer): O(1)
// Append (with known tail pointer): O(1)
export class ListNode {
val: number;
next: ListNode | null;
constructor(val?: number, next?: ListNode | null) {
this.val = val === undefined ? 0 : val;
this.next = next === undefined ? null : next;
}
}
// Floyd's Tortoise and Hare Cycle Detection Algorithm: O(n) time, O(1) space
export function hasCycle(head: ListNode | null): boolean {
let slow: ListNode | null = head;
let fast: ListNode | null = head;
while (fast !== null && fast.next !== null) {
slow = slow!.next;
fast = fast.next.next;
if (slow === fast) {
return true; // Cycle detected
}
}
return false;
}Do:Use dummy head nodes to simplify edge cases
Creating a `dummy = new ListNode(0)` before the actual head eliminates special-casing for head insertions and deletions.
Avoid:Overwriting pointer references before saving next nodes
Always store `const nextNode = curr.next` in a temporary variable before mutating `curr.next` to avoid breaking the chain.
Pro Tip for Beginners
Common Beginner Pitfall & How to Fix It
What happens: Null pointer dereference (`cannot read property of null`)
How to solve it: Always verify `fast !== null && fast.next !== null` when advancing multi-step pointers.
Hands-On Practice Checklist
Try each of these steps on your computer and check them off as you complete them:
Queue (FIFO), Double-Ended Deque & Level-Order BFS
Master First-In, First-Out (FIFO) processing, circular queue buffers, double-ended deques, and Breadth-First Search (BFS) traversals.
A Queue is a linear First-In, First-Out (FIFO) container where elements are inserted at the back (`enqueue`) and removed from the front (`dequeue`). It mirrors real-world waiting lines—the first item added is the first one served. Queues are the foundational engine for Breadth-First Search (BFS), level-by-level tree/graph traversals, task scheduling, and sliding window maximums. Because standard arrays incur O(n) shifts when removing from index 0, production queues are implemented using Linked Lists or circular ring buffers with separate head and tail pointers.
1Step 1: Enqueue Elements at the Rear
$ Enqueue sequence: 10 → 20 → 302Step 2: Dequeue Elements from the Front
$ Dequeue from QueueBinary Tree Level Order Traversal using a FIFO Queue to process tree levels batch-by-batch in O(n) time and O(w) space.
// Queue Complexities:
// Enqueue (Insert at Rear): O(1)
// Dequeue (Remove from Front): O(1)
// Peek Front: O(1) | Search: O(n)
export function levelOrderTraversal(root: { val: number; left: any; right: any } | null): number[][] {
if (!root) return [];
const result: number[][] = [];
const queue: any[] = [root];
while (queue.length > 0) {
const levelSize = queue.length;
const currentLevel: number[] = [];
for (let i = 0; i < levelSize; i++) {
const node = queue.shift(); // Dequeue from front
currentLevel.push(node.val);
if (node.left) queue.push(node.left); // Enqueue left child
if (node.right) queue.push(node.right); // Enqueue right child
}
result.push(currentLevel);
}
return result;
}Do:Record level size before iterating child nodes in BFS
Snapshotting `const levelSize = queue.length` ensures you only process nodes belonging to the current depth level.
Avoid:Using `array.shift()` in high-performance loops with large N
In JavaScript/Python, `array.shift()` or `list.pop(0)` is O(n). In production, use a Doubly Linked List, `collections.deque`, or index-pointer queues.
Pro Tip for Beginners
Common Beginner Pitfall & How to Fix It
What happens: Confusing Monotonic Stack with Monotonic Queue
How to solve it: Use a Monotonic Stack for next greater element in arrays; use a Monotonic Deque when elements expire out of a sliding window.
Hands-On Practice Checklist
Try each of these steps on your computer and check them off as you complete them:
Hash Tables & Fast Key-Value Lookups
Master key-value hashing mechanics, bucket arrays, collision resolution (chaining vs open addressing), and converting O(n²) loops into O(1) lookups.
A Hash Table (Hash Map) stores key-value pairs indexed by key rather than contiguous position. A hash function transforms any arbitrary hashable key into an integer bucket index within an underlying array. When designed properly, insertion, retrieval, and deletion execute in average O(1) constant time. In interviews, Hash Maps are the single most powerful tool for converting O(n²) nested brute-force loops into linear O(n) algorithms. The trade-off is that Hash Maps are unordered (keys are not sorted) and worst-case performance degrades to O(n) if all keys collide into the same bucket.
1Step 1: Compute Hash Function & Bucket Placement
$ Insert key 'user_42' with Value { role: 'admin' }2Step 2: Understand Hash Collision Resolution via Chaining
$ Insert key 'item_99' where Hash('item_99') % 16 also equals Bucket 5Two Sum solved in O(n) time and O(n) space by replacing quadratic nested loops with O(1) Hash Map complement lookups.
// Hash Map Complexities:
// Insert / Lookup / Delete / ContainsKey: Average O(1) | Worst O(n) on full collisions
// Space Complexity: O(n) memory overhead for bucket table
export function twoSumHashMap(nums: number[], target: number): number[] {
const seenMap = new Map<number, number>(); // Value -> Index
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];
if (seenMap.has(complement)) {
return [seenMap.get(complement)!, i];
}
seenMap.set(nums[i], i);
}
return [];
}Do:Ensure keys are immutable and properly hashable
Primitive types (strings, numbers, booleans) are naturally hashable. In languages like C++/Java, custom object keys require implementing `hash()` and `equals()`.
Avoid:Assuming Hash Maps maintain sorted or insertion order
Standard hash tables do not guarantee iteration order. If sorted key traversal is required, use a Binary Search Tree (Red-Black Tree) or LinkedHashMap.
Pro Tip for Beginners
Common Beginner Pitfall & How to Fix It
What happens: Using unhashable types (e.g. raw arrays or tuples) as map keys in JavaScript
How to solve it: Serialize composite keys into strings (e.g. `${row},${col}`) or use a nested Map.
Hands-On Practice Checklist
Try each of these steps on your computer and check them off as you complete them:
Binary Trees, Binary Search Trees (BST) & Tree Traversals
Master hierarchical tree representations, the BST invariant (left < root < right), tree traversals (In-Order, Pre-Order, Post-Order), and balanced vs skewed trees.
A Tree is a hierarchical non-linear data structure of connected nodes starting from a single `root`. In a Binary Search Tree (BST), every node adheres to the BST Invariant: all keys in the left subtree are strictly smaller than the node, and all keys in the right subtree are strictly greater than the node. This invariant allows search, insertion, and deletion to execute in O(log n) time by eliminating half of the remaining subtree at every step. However, if elements arrive in sorted order, an unbalanced BST degrades into a linear linked list with O(n) complexity. Self-balancing variants (AVL Trees and Red-Black Trees) perform rotations to maintain O(log n) guarantees.
1Step 1: Verify the BST Ordering Invariant
$ Inspect node with Value = 50. Left Child = 30, Right Child = 702Step 2: Understand In-Order Traversal Yielding Sorted Output
$ Perform In-Order Traversal (Left -> Root -> Right) on BST [50, 30, 70, 20, 40, 60, 80]Validating a Binary Search Tree recursively by enforcing lower and upper boundary constraints across subtrees in O(n) time.
// BST Complexities:
// Search / Insert / Delete (Balanced): O(log n) | Skewed Worst-Case: O(n)
// Tree Traversals (In-Order, Pre-Order, Post-Order): O(n) time visiting all nodes
export class TreeNode {
val: number;
left: TreeNode | null;
right: TreeNode | null;
constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
this.val = val === undefined ? 0 : val;
this.left = left === undefined ? null : left;
this.right = right === undefined ? null : right;
}
}
export function isValidBST(root: TreeNode | null, min: number = -Infinity, max: number = Infinity): boolean {
if (!root) return true;
if (root.val <= min || root.val >= max) return false;
return (
isValidBST(root.left, min, root.val) &&
isValidBST(root.right, root.val, max)
);
}Do:Remember that In-Order traversal of a BST produces sorted data
Whenever an interview problem asks for sorted elements or finding the Kth smallest element in a BST, immediately consider In-Order traversal.
Avoid:Validating BSTs by only checking immediate left and right children
A valid BST requires ALL descendants in the left subtree to be smaller than the root, not just the direct left child. Always pass down valid range bounds.
Pro Tip for Beginners
Common Beginner Pitfall & How to Fix It
What happens: Forgetting that BST operations degrade to O(n) on skewed inputs
How to solve it: Acknowledge in interviews that plain BSTs can skew into linked lists (e.g. inserting 1, 2, 3, 4, 5) and mention balanced AVL / Red-Black trees.
Hands-On Practice Checklist
Try each of these steps on your computer and check them off as you complete them:
Heaps & Priority Queues: Min-Heap, Max-Heap & Streaming Extrema
Master complete binary tree array representations, Min-Heap vs Max-Heap invariants, O(1) extrema access, bubble-up/sift-down heapify, and Top-K patterns.
A Heap is a complete binary tree that maintains partial ordering to provide instant O(1) access to the minimum element (Min-Heap) or maximum element (Max-Heap). In a Min-Heap, every parent node is less than or equal to its children, ensuring the absolute minimum element always sits at the root. Heaps are stored compactly inside arrays without pointer overhead using parent-child arithmetic (`Left = 2*i + 1`, `Right = 2*i + 2`, `Parent = Math.floor((i - 1) / 2)`). Inserting an element (`push` / bubble-up) and extracting the root (`pop` / sift-down) both take O(log n) time. Building a heap from an unsorted array (`heapify`) takes O(n) time.
1Step 1: Inspect Array-Based Heap Index Math
$ Array Heap: [10, 25, 15, 30, 40, 20, 50]. Inspect Node at Index 0 (Root = 10)2Step 2: Extract Extrema & Restore Heap via Sift-Down
$ Extract Minimum (10) from Min-HeapComplete Min-Heap implementation from scratch with bubble-up and sift-down mechanics.
// Heap / Priority Queue Complexities:
// Peek Min/Max: O(1)
// Insert / Extract Min/Max: O(log n)
// Build Heap (Heapify): O(n)
// Arbitrary Element Search: O(n) (Heaps are only partially ordered!)
// Find Kth Largest Element using a Min-Heap of size K: O(n log k) time, O(k) space
export class SimpleMinHeap {
private data: number[] = [];
push(val: number): void {
this.data.push(val);
this.bubbleUp(this.data.length - 1);
}
pop(): number | undefined {
if (this.data.length === 0) return undefined;
const min = this.data[0];
const last = this.data.pop()!;
if (this.data.length > 0) {
this.data[0] = last;
this.siftDown(0);
}
return min;
}
peek(): number { return this.data[0]; }
size(): number { return this.data.length; }
private bubbleUp(idx: number): void {
while (idx > 0) {
const parentIdx = Math.floor((idx - 1) / 2);
if (this.data[idx] < this.data[parentIdx]) {
[this.data[idx], this.data[parentIdx]] = [this.data[parentIdx], this.data[idx]];
idx = parentIdx;
} else break;
}
}
private siftDown(idx: number): void {
const length = this.data.length;
while (true) {
let smallest = idx;
const left = 2 * idx + 1;
const right = 2 * idx + 2;
if (left < length && this.data[left] < this.data[smallest]) smallest = left;
if (right < length && this.data[right] < this.data[smallest]) smallest = right;
if (smallest !== idx) {
[this.data[idx], this.data[smallest]] = [this.data[smallest], this.data[idx]];
idx = smallest;
} else break;
}
}
}Do:Use a size-K Min-Heap to find the Kth largest element
Maintaining a Min-Heap of size K as you scan N elements processes streaming data in O(N log K) time and O(K) space instead of O(N log N) full sorting.
Avoid:Assuming a Heap is completely sorted
A heap only guarantees that the root is the extreme element. Sibling and subtree relationships are not sorted relative to each other.
Pro Tip for Beginners
Common Beginner Pitfall & How to Fix It
What happens: Attempting to switch between Min-Heap and Max-Heap dynamically on the fly
How to solve it: Heaps fix their comparator at initialization. For Max-Heaps in languages with only Min-Heaps (like Python `heapq`), invert numbers by multiplying by -1.
Hands-On Practice Checklist
Try each of these steps on your computer and check them off as you complete them:
Graph Topologies, Adjacency Representations & Traversals
Master vertices and edges, Adjacency Lists vs Matrices, Breadth-First & Depth-First Search, cycle detection, visited sets, and Topological Sorting (DAGs).
A Graph is a versatile non-linear network consisting of a set of vertices (nodes $V$) connected by edges ($E$). Graphs model complex relationships such as social networks, road maps, network packets, prerequisite dependencies, and connected islands. Graphs can be Directed or Undirected, Weighted or Unweighted, and Cyclic or Acyclic. The standard representation is an Adjacency List (Map of Node to List of Neighbors). Traversal is performed using BFS (queue-based) or DFS (recursion/stack-based) in $O(V + E)$ time. In all graph traversals, maintaining a `visited` set is mandatory to prevent infinite recursion loops caused by cycles.
1Step 1: Construct an Adjacency List Representation
$ Graph edges: A -> B, A -> C, B -> D, C -> D2Step 2: Execute BFS with Visited Set Tracking
$ Traverse from node 'A' using BFSKahn's Algorithm for Topological Sort and Cycle Detection in Directed Graphs using in-degree tracking in O(V + E) time.
// Graph Complexities:
// BFS / DFS Traversal: O(V + E)
// Cycle Detection: O(V + E)
// Topological Sort (Kahn's / DFS): O(V + E)
// Dijkstra's Shortest Path: O((V + E) log V) | Bellman-Ford: O(V * E)
// Course Schedule (Topological Sort / Cycle in Directed Acyclic Graph)
export function canFinishCourses(numCourses: number, prerequisites: number[][]): boolean {
const inDegree = new Array(numCourses).fill(0);
const adjList = new Map<number, number[]>();
for (const [course, prereq] of prerequisites) {
if (!adjList.has(prereq)) adjList.set(prereq, []);
adjList.get(prereq)!.push(course);
inDegree[course]++;
}
const queue: number[] = [];
for (let i = 0; i < numCourses; i++) {
if (inDegree[i] === 0) queue.push(i);
}
let completedCount = 0;
while (queue.length > 0) {
const current = queue.shift()!;
completedCount++;
const neighbors = adjList.get(current) || [];
for (const neighbor of neighbors) {
inDegree[neighbor]--;
if (inDegree[neighbor] === 0) {
queue.push(neighbor);
}
}
}
return completedCount === numCourses;
}Do:Always maintain a visited set during graph traversals
Cyclic graphs will trap BFS and DFS in infinite execution loops unless visited vertices are tracked.
Avoid:Using an Adjacency Matrix when V is large and E is small
An Adjacency Matrix takes O(V²) space, which is wasteful for sparse graphs. Use an Adjacency List for O(V + E) space efficiency.
Pro Tip for Beginners
Common Beginner Pitfall & How to Fix It
What happens: Forgetting to initialize empty adjacency lists for isolated nodes
How to solve it: Ensure your adjacency list initializes all vertices $0$ through $V-1$ so disconnected vertices are not missed.
Hands-On Practice Checklist
Try each of these steps on your computer and check them off as you complete them:
Trie (Prefix Tree) Architecture & String Retrieval
Master Trie node structures, shared prefix compression, O(m) word insertion/search independent of dictionary size, and autocomplete engines.
A Trie (Prefix Tree) is a specialized tree data structure designed for efficient string retrieval and prefix matching. In a Trie, each node contains a map or fixed array of child pointers (e.g. 26 lowercase English letters) and an `isEndOfWord` boolean flag. Strings sharing common prefixes branch from the same root nodes (e.g. 'cat' and 'car' share the prefix path 'c' -> 'a'). Inserting a word, searching for a word, and checking if a prefix exists all take O(m) time where $m$ is the length of the string—completely independent of how many millions of words exist in the dictionary. While Tries consume extra memory for sparse branches, they are unbeatable for typeahead autocomplete, spellcheckers, and IP routing tables.
1Step 1: Insert Words with Shared Prefix into Trie
$ Insert 'CAT' followed by 'CAR'2Step 2: Perform O(m) Prefix Lookup (`startsWith`)
$ Query `startsWith('CA')`Clean Trie implementation supporting insert, exact search, and prefix matching in O(m) time.
// Trie Complexities:
// Insert Word: O(m) where m = word length
// Search Word: O(m)
// StartsWith (Prefix Exists): O(m)
// Space Complexity: O(ALPHABET_SIZE * m * N) where N = number of keys
export class TrieNode {
children: Map<string, TrieNode> = new Map();
isEndOfWord: boolean = false;
}
export class Trie {
root: TrieNode = new TrieNode();
insert(word: string): void {
let curr = this.root;
for (const char of word) {
if (!curr.children.has(char)) {
curr.children.set(char, new TrieNode());
}
curr = curr.children.get(char)!;
}
curr.isEndOfWord = true;
}
search(word: string): boolean {
let curr = this.root;
for (const char of word) {
if (!curr.children.has(char)) return false;
curr = curr.children.get(char)!;
}
return curr.isEndOfWord;
}
startsWith(prefix: string): boolean {
let curr = this.root;
for (const char of prefix) {
if (!curr.children.has(char)) return false;
curr = curr.children.get(char)!;
}
return true;
}
}Do:Use a Trie when problems require prefix matching or autocomplete
If a problem mentions 'startsWith', 'longest common prefix', or 'autocomplete suggest', a Trie is almost always the optimal solution.
Avoid:Using Tries for single one-shot exact lookups with sparse strings
For simple exact-match lookups without prefix queries, a standard Hash Map is faster, uses less memory, and is simpler to implement.
Pro Tip for Beginners
Common Beginner Pitfall & How to Fix It
What happens: Confusing `search(word)` with `startsWith(prefix)`
How to solve it: Exact `search()` requires `curr.isEndOfWord === true` at the final node; `startsWith()` only requires reaching the final character node.
Hands-On Practice Checklist
Try each of these steps on your computer and check them off as you complete them:
The Master Interview Decision Matrix & Complexity Cheat Sheet
Master the high-yield decision framework to instantly identify the correct data structure within the first 60 seconds of any technical interview.
When presented with a coding interview problem, top candidates follow a systematic decision framework rather than guessing. By mapping problem constraints and key phrases directly to the operational strengths of the 9 core data structures, you can pinpoint the optimal data structure and complexity target immediately. This module provides the consolidated master Big-O operational complexity matrix, data structure selection flowchart, and a practical 5-step problem solving blueprint.
1Step 1: Execute the 60-Second Keyword Pattern Filter
$ Problem phrase: 'Find the top K most frequent elements in a continuous stream of numbers'2Step 2: Verify Big-O Operational Bounds Against N Constraints
$ Constraint Check: N <= 10^5. Target Time: O(N) or O(N log N)Consolidated Big-O runtime and space complexity reference table for all 9 foundational data structures.
// MASTER DATA STRUCTURE COMPLEXITY MATRIX (Cheat Sheet Reference) // ------------------------------------------------------------------------------------------------ // Data Structure | Access | Search | Insertion | Deletion | Space Overhead // ------------------------------------------------------------------------------------------------ // Array | O(1) | O(n) | O(n) mid | O(n) mid | O(1) Contiguous // Stack (LIFO) | O(n) / O(1)T| O(n) | O(1) push | O(1) pop | O(n) // Queue (FIFO) | O(n) / O(1)F| O(n) | O(1) rear | O(1) front | O(n) // Linked List | O(n) | O(n) | O(1) ref | O(1) ref | O(n) Pointers // Hash Map | N/A | O(1) avg | O(1) avg | O(1) avg | O(n) Buckets // BST (Balanced) | O(log n) | O(log n) | O(log n) | O(log n) | O(n) Pointers // Heap (Min/Max) | O(1) root | O(n) | O(log n) | O(log n) | O(1) Array-backed // Graph (Adj List) | N/A | O(V + E) | O(1) edge | O(E) edge | O(V + E) // Trie | N/A | O(m) word | O(m) word | O(m) word | O(Alphabet * m * N) // ------------------------------------------------------------------------------------------------
Do:Communicate time and space trade-offs before writing code
State your proposed data structure, time complexity, and space complexity clearly to the interviewer before starting implementation.
Avoid:Jumping directly into code without checking edge cases
Always check empty inputs, single-element collections, duplicate values, negative numbers, and boundary limits.
Pro Tip for Beginners
Common Beginner Pitfall & How to Fix It
What happens: Premature optimization before establishing a correct working approach
How to solve it: Quickly outline the brute-force baseline first, identify the performance bottleneck, and explain how the chosen data structure optimizes it.
Hands-On Practice Checklist
Try each of these steps on your computer and check them off as you complete them:
Masterclass Starter Files & Templates
Data Structures & Algorithms Cheat Sheet Master Pack
Includes master Big-O complexity lookup tables, 9 foundational data structure boilerplates in TypeScript, Python & C++, and quick-reference problem trigger maps.