SkillCharged
Data Structures & Algorithms Masterclass
Algorithms & Data Structures
Data Structures
Algorithms
Arrays
Linked Lists
Stacks & Queues
Hash Maps
Trees & BST
Heaps
Graphs
Tries
Time Complexity
Interview Prep

Data Structures & Algorithms Cheat Sheet (2026): The Complete Technical Interview Masterclass

Guided by Nitin Khatri (Lead Software Architect & Designer)
Reviewed by Sarah Miller (Principal AI Engineer, Reviewer)
Updated August 1, 2026
60 min
Level: Intermediate

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
Core Concept

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

Traditional Approach:Contiguous Static Allocation: Elements placed side-by-side in fixed memory blocks with direct offset jumping (Arrays).
Data Structures & Algorithms:Dynamic Pointer-Chained Allocation: Nodes scattered across heap memory connected via memory addresses (Linked Lists, Trees, Graphs).

Element Access Mechanism

Traditional Approach:Positional Index Offsets: O(1) direct mathematical indexing based on base address + index * element_size.
Data Structures & Algorithms:Key-Hashed or Hierarchical Search: O(1) average hash bucket lookups or O(log N) branch-pruning binary traversals.

Interview Problem Strategy

Traditional Approach:Brute-force nested loops resulting in O(N²) or O(N³) timeouts and unscalable memory allocations.
Data Structures & Algorithms:Keyword Pattern Matching: Triggering optimal data structures directly from problem wording (e.g. 'top K' → Heap, 'undo/balanced' → Stack).

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 / TriggerWhat It Does
Array: arr[i]O(1) random access via memory address math (base + i * size). Middle insert/delete: O(n).
Stack: push / pop / topLIFO order. O(1) top access. Triggered by undo, brackets, DFS, monotonic patterns.
Queue: enqueue / dequeueFIFO order. O(1) push rear / pop front. Triggered by BFS, level-order, task scheduling.
Linked List: head -> nextO(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 < rightO(log n) balanced search/insert. Degrades to O(n) if skewed. In-order traversal yields sorted sequence.
Heap: peek min/maxO(1) extrema access, O(log n) insert/extract. Triggered by 'Top K', 'Kth largest', 'Median in stream'.
Graph: BFS / DFS + visitedO(V + E) traversal. Always track visited nodes to avoid infinite cycles in cyclic topologies.
Trie: root -> char branchesO(m) prefix search where m is word length. Completely independent of total dictionary size.
Module 1

Array Mechanics & Contiguous Memory Architecture

Module Learning Goal

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

Try this prompt in your agent:
$ Base Address = 0x5BC0, Element Size = 4 bytes (int). Target: Index 3
What you will see on screen:
Target Address = 0x5BC0 + (3 * 4 bytes) = 0x5BC0 + 12 (0x0C) = 0x5BCC Result: Direct 1-step CPU memory jump. Time Complexity: O(1).
Under the hood:Because memory is contiguous, the CPU does not traverse intermediate elements—it directly reads the calculated memory address in a single step.

2Step 2: Understand Element Shifting Overhead on Middle Modification

Try this prompt in your agent:
$ Delete element at index 1 from array [10, 20, 30, 40]
What you will see on screen:
Step 1: Remove value 20 at index 1. Step 2: Shift value 30 from index 2 to index 1. Step 3: Shift value 40 from index 3 to index 2. Total shifts: 2 elements. Time Complexity: O(n).
Under the hood:Arrays have no gaps in contiguous memory. Deleting or inserting any element except at the tail requires moving all subsequent elements to maintain contiguous layout.
array_operations.tstypescript

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

🔍
Interview Keyword Triggers for Arrays:Look for keywords: 'index', 'sliding window', 'two pointer', 'subarray', 'prefix sum', 'kth element', 'in-place', and 'circular buffer'.
🎯
Must-Solve Classic Array Problems:Two Sum, Best Time to Buy and Sell Stock, Maximum Subarray (Kadane's Algorithm), Rotate Array, and Product of Array Except Self.

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:

Module 2

Stack (LIFO) & Function Call Stack Mechanics

Module Learning Goal

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

Try this prompt in your agent:
$ Push sequence: 'A' → 'B' → 'C' → 'D'
What you will see on screen:
[Bottom] A -> B -> C -> D [Top] Top pointer = 'D'. Time Complexity: O(1).
Under the hood:Each push increments the top pointer and stores the new value in O(1) constant time.

2Step 2: Pop & Inspect Extrema

Try this prompt in your agent:
$ Pop from Stack
What you will see on screen:
Popped element: 'D' New Top pointer = 'C' Stack State: [Bottom] A -> B -> C [Top]. Time Complexity: O(1).
Under the hood:Popping retrieves the most recently added element without having to scan previous items.
valid_parentheses.tstypescript

Canonical 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

Interview Keyword Triggers for Stacks:Look for keywords: 'undo', 'balanced brackets', 'reverse', 'backtracking', 'DFS / call stack', 'monotonic stack', and 'next greater element'.
🎯
Must-Solve Classic Stack Problems:Valid Parentheses, Min Stack (O(1) getMin), Daily Temperatures (Monotonic Stack), Largest Rectangle in Histogram, and Next Greater Element.

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:

Module 3

Linked Lists (Singly & Doubly) & Dynamic Memory Rewiring

Module Learning Goal

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

Try this prompt in your agent:
$ Node A [Val: 10 | Next: 0xC012] -> Node B [Val: 20 | Next: 0x8F40] -> Node C [Val: 30 | Next: null]
What you will see on screen:
Heap Allocation: Non-contiguous. Node A Address: 0x5B10 (Head) Node B Address: 0xC012 Node C Address: 0x8F40 (Tail)
Under the hood:Each node lives independently in heap memory. Losing the `next` pointer causes a memory leak as orphaned nodes become unreachable.

2Step 2: O(1) Node Insertion via Pointer Rewiring

Try this prompt in your agent:
$ Insert Node X [Val: 15] between Node A and Node B
What you will see on screen:
Step 1: Set Node X.next = Node A.next (0xC012) Step 2: Set Node A.next = Address of Node X Result: Node A -> Node X -> Node B. Operations: 2 pointer updates. Time Complexity: O(1).
Under the hood:No element shifting is required; only two pointer references are reassigned.
linked_list_patterns.tstypescript

Singly 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

🔗
Interview Keyword Triggers for Linked Lists:Look for keywords: 'reverse list', 'cycle detection', 'fast & slow pointers', 'middle element', 'merge sorted lists', 'dummy head', and 'palindrome list'.
🎯
Must-Solve Classic Linked List Problems:Reverse Linked List, Linked List Cycle Detection (Floyd's), Merge Two Sorted Lists, Remove Nth Node From End of List, and LRU Cache.

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:

Module 4

Queue (FIFO), Double-Ended Deque & Level-Order BFS

Module Learning Goal

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

Try this prompt in your agent:
$ Enqueue sequence: 10 → 20 → 30
What you will see on screen:
[Front] 10 -> 20 -> 30 [Rear] Front = 10, Rear = 30. Time Complexity: O(1).
Under the hood:New elements are appended to the rear of the queue in O(1) time.

2Step 2: Dequeue Elements from the Front

Try this prompt in your agent:
$ Dequeue from Queue
What you will see on screen:
Dequeued element: 10 New Front = 20 Queue State: [Front] 20 -> 30 [Rear]. Time Complexity: O(1).
Under the hood:The earliest arrived element is processed and removed from the front in O(1) time without shifting.
bfs_level_order.tstypescript

Binary 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

🚶
Interview Keyword Triggers for Queues:Look for keywords: 'BFS', 'level-order traversal', 'shortest path (unweighted grid)', 'task scheduler', 'sliding window maximum', and 'monotonic queue'.
🎯
Must-Solve Classic Queue Problems:Binary Tree Level Order Traversal, Sliding Window Maximum (Monotonic Deque), Task Scheduler, and Number of Islands (BFS).

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:

Module 5

Hash Tables & Fast Key-Value Lookups

Module Learning Goal

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

Try this prompt in your agent:
$ Insert key 'user_42' with Value { role: 'admin' }
What you will see on screen:
Step 1: Hash('user_42') = 982341 Step 2: Bucket Index = 982341 % ArraySize(16) = Bucket 5 Step 3: Store entry at Bucket 5. Time Complexity: O(1) avg.
Under the hood:The hash function maps the string key to a deterministic numeric index in constant time.

2Step 2: Understand Hash Collision Resolution via Chaining

Try this prompt in your agent:
$ Insert key 'item_99' where Hash('item_99') % 16 also equals Bucket 5
What you will see on screen:
Collision detected at Bucket 5. Resolution: Append 'item_99' to the linked list chain at Bucket 5. Bucket 5: ['user_42'] -> ['item_99'] -> null.
Under the hood:Chaining attaches colliding entries to a linked list or tree within the target bucket.
hashmap_twosum.tstypescript

Two 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

🔑
Interview Keyword Triggers for Hash Maps:Look for keywords: 'frequency count', 'seen / visited elements', 'memoization cache', 'group by', 'anagram', 'two sum', and 'find duplicates'.
🎯
Must-Solve Classic Hash Map Problems:Two Sum, Group Anagrams, Longest Substring Without Repeating Characters, Top K Frequent Elements, Subarray Sum Equals K, and LRU Cache.

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:

Module 6

Binary Trees, Binary Search Trees (BST) & Tree Traversals

Module Learning Goal

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

Try this prompt in your agent:
$ Inspect node with Value = 50. Left Child = 30, Right Child = 70
What you will see on screen:
Left Subtree (all < 50): 30 -> [20, 40] Right Subtree (all > 50): 70 -> [60, 80] Result: Valid BST. Search space halves at every branching step.
Under the hood:Comparing target values against node values enables O(log n) binary search traversal.

2Step 2: Understand In-Order Traversal Yielding Sorted Output

Try this prompt in your agent:
$ Perform In-Order Traversal (Left -> Root -> Right) on BST [50, 30, 70, 20, 40, 60, 80]
What you will see on screen:
Visit sequence: 20 → 30 → 40 → 50 → 60 → 70 → 80 Result: Strictly ascending sorted sequence. Time Complexity: O(n).
Under the hood:In-Order traversal of any valid BST always produces keys in sorted ascending order.
bst_validation.tstypescript

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

🌲
Interview Keyword Triggers for Trees & BSTs:Look for keywords: 'hierarchy', 'parent/child', 'depth/height', 'recursion / DFS', 'in-order / pre-order / post-order', 'LCA (Lowest Common Ancestor)', and 'balanced tree'.
🎯
Must-Solve Classic Tree Problems:Maximum Depth of Binary Tree, Same Tree, Invert Binary Tree, Lowest Common Ancestor (LCA), Validate Binary Search Tree, and Path Sum.

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:

Module 7

Heaps & Priority Queues: Min-Heap, Max-Heap & Streaming Extrema

Module Learning Goal

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

Try this prompt in your agent:
$ Array Heap: [10, 25, 15, 30, 40, 20, 50]. Inspect Node at Index 0 (Root = 10)
What you will see on screen:
Left Child Index = 2*0 + 1 = 1 (Val: 25) Right Child Index = 2*0 + 2 = 2 (Val: 15) Parent invariant holds: 10 <= 25 and 10 <= 15.
Under the hood:Complete binary trees can be stored directly in contiguous arrays without node pointer structures.

2Step 2: Extract Extrema & Restore Heap via Sift-Down

Try this prompt in your agent:
$ Extract Minimum (10) from Min-Heap
What you will see on screen:
Step 1: Remove Root (10). Move last element (50) to Root. Step 2: Compare 50 with children (25, 15). Swap with smaller child (15). Step 3: Heap invariant restored. Time Complexity: O(log n).
Under the hood:Sift-down swaps the out-of-place root down the tree until the Min-Heap property is restored in logarithmic time.
kth_largest_heap.tstypescript

Complete 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

🏔️
Interview Keyword Triggers for Heaps:Look for keywords: 'top K elements', 'Kth largest / Kth smallest', 'priority queue', 'median in a stream', 'merge K sorted lists', and 'K closest points'.
🎯
Must-Solve Classic Heap Problems:Kth Largest Element in an Array, Top K Frequent Elements, Merge K Sorted Lists, Find Median from Data Stream (Two Heaps), and K Closest Points to Origin.

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:

Module 8

Graph Topologies, Adjacency Representations & Traversals

Module Learning Goal

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

Try this prompt in your agent:
$ Graph edges: A -> B, A -> C, B -> D, C -> D
What you will see on screen:
Adjacency List: 'A': ['B', 'C'] 'B': ['D'] 'C': ['D'] 'D': [] Space Complexity: O(V + E).
Under the hood:Adjacency lists store only existing edges, making them memory efficient for sparse graphs compared to V×V matrices.

2Step 2: Execute BFS with Visited Set Tracking

Try this prompt in your agent:
$ Traverse from node 'A' using BFS
What you will see on screen:
Step 1: Queue = ['A'], Visited = {'A'} Step 2: Pop 'A'. Enqueue unvisited neighbors 'B', 'C'. Visited = {'A', 'B', 'C'} Step 3: Pop 'B'. Enqueue unvisited neighbor 'D'. Visited = {'A', 'B', 'C', 'D'} Step 4: Pop 'C'. Neighbor 'D' is already in Visited -> Skip! Traversal complete in O(V + E) without cycle traps.
Under the hood:The visited set prevents processing duplicate nodes and eliminates infinite loop cycles.
topological_sort.tstypescript

Kahn'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

🌐
Interview Keyword Triggers for Graphs:Look for keywords: 'vertices & edges', 'network', 'connection / path', 'connected components / islands', 'cycle detection', 'dependency resolution', 'topological sort', and 'shortest path'.
🎯
Must-Solve Classic Graph Problems:Number of Islands, Clone Graph, Course Schedule (Topological Sort), Word Ladder, Pacific Atlantic Water Flow, and Network Delay Time.

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:

Module 9

Trie (Prefix Tree) Architecture & String Retrieval

Module Learning Goal

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

Try this prompt in your agent:
$ Insert 'CAT' followed by 'CAR'
What you will see on screen:
Root -> 'C' -> 'A' -> 'T' (isEnd: true) ↳ 'R' (isEnd: true) Shared prefix 'CA' is stored once. Time Complexity: O(m) where m = word length.
Under the hood:Both words reuse the common 'C' and 'A' node chain, branching only on the final character.

2Step 2: Perform O(m) Prefix Lookup (`startsWith`)

Try this prompt in your agent:
$ Query `startsWith('CA')`
What you will see on screen:
Step 1: Walk Root -> 'C' -> 'A' Step 2: Node 'A' exists! Result: true. Time Complexity: O(m).
Under the hood:Prefix validation only requires walking $m$ character edges without scanning other words in the dictionary.
trie_implementation.tstypescript

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

🔤
Interview Keyword Triggers for Tries:Look for keywords: 'prefix', 'autocomplete', 'typeahead / suggest', 'starts with', 'dictionary word validation', 'longest common prefix', and 'word search grid'.
🎯
Must-Solve Classic Trie Problems:Implement Trie (Prefix Tree), Word Search II (Trie + Backtracking Matrix), Design Add and Search Words Data Structure, Replace Words, and Maximum XOR of Two Numbers in an Array.

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:

Module 10

The Master Interview Decision Matrix & Complexity Cheat Sheet

Module Learning Goal

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

Try this prompt in your agent:
$ Problem phrase: 'Find the top K most frequent elements in a continuous stream of numbers'
What you will see on screen:
Pattern Triggers Detected: 1. 'Top K' -> Priority Queue / Heap 2. 'Frequency count' -> Hash Map 3. 'Continuous stream' -> Dynamic Size-K Min-Heap Optimal Architecture: Hash Map for counts + Size-K Min-Heap for streaming top K. Overall Time: O(N log K).
Under the hood:Combining keyword filters instantly narrows down the exact multi-structure solution.

2Step 2: Verify Big-O Operational Bounds Against N Constraints

Try this prompt in your agent:
$ Constraint Check: N <= 10^5. Target Time: O(N) or O(N log N)
What you will see on screen:
O(N^2) Brute Force -> 10^10 operations -> TLE (Time Limit Exceeded)! O(N log N) Heap / Sort -> ~1.6 * 10^6 operations -> PASSES comfortably (< 10^8 operations per second). Result: Algorithm approved for implementation.
Under the hood:Technical interviews test your ability to guarantee time/space feasibility before writing code.
complexity_matrix.tstypescript

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

🎯
The 5-Step Interview Problem Solving Framework:1. Clarify inputs, outputs, and constraints -> 2. Identify keyword pattern triggers -> 3. State optimal data structure and Big-O bounds -> 4. Implement clean, modular code -> 5. Dry-run test cases and boundary conditions.

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.

Download DSA Cheat Sheet (.zip)

Ready to Take Your Skills Further?

Explore our developer guides, reference playbooks, and video courses to keep leveling up.