RSRobince Studio

Data Structures interview revision notes

Interview-focused Data Structures revision. Written for developers preparing for an interview who want to refresh their Data Structures fast. Free, no sign-up.

20 topics in these notes

  1. What are data structures?
  2. Big O notation
  3. Array
  4. String (common operations)
  5. Linked List
  6. Stack
  7. Queue
  8. Hash Table
  9. Tree & Binary Tree
  10. Binary Search Tree (BST)
  11. Heap
  12. Graph (BFS, DFS)
  13. Trie
  14. Recursion
  15. Two pointers & sliding window
  16. Binary search
  17. Sorting (Quick, Merge)
  18. Dynamic programming
  19. Greedy
  20. Common patterns

What are data structures?

Simple real-life example (Understand first — very important for understanding)

  • Think of data structures as different ways to organize and store data so that certain operations (finding, adding, sorting) are easy or fast.
  • Like choosing the right container: a list for order, a stack for undo, a queue for waiting in line, a tree for hierarchy.
  • Picking the right structure makes your program simpler and faster.

Real-world idea: the right organization for the job. Data structures are how we arrange data so we can work with it efficiently.

Technical definition

  • Data structures are ways to organize and store data so that operations (insert, search, delete, traverse) can be done efficiently.
  • Choice of structure affects time (how fast) and space (how much memory). Different problems need different structures (e.g. hash for O(1) lookup, tree for ordered data).
  • Common categories: linear (array, list, stack, queue), tree (binary tree, BST, heap), graph, hash-based. Plus techniques: recursion, two pointers, binary search, DP, greedy.

Simple practical example

Choice of structure affects time (how fast) and space (how much memory).

Code example

// Need fast lookup by key? → Hash table
// Need order / range queries? → BST or sorted array + binary search
// Need LIFO (undo)? → Stack

Why it matters

Coding interviews focus on picking the right structure and justifying time/space complexity. "Which data structure would you use?" is very common.

Interview tip

"Pick structure by operations you need: array for index access, hash for lookup, stack/queue for order, tree for sorted/ hierarchical."

Big O notation

Simple real-life example (Understand first — very important for understanding)

  • Like describing how long a task takes as the amount of data grows: "double the list, double the time" vs "double the list, same time."
  • Big O gives a simple way to compare algorithms: O(1) constant, O(n) linear, O(n²) quadratic, etc. It ignores constants and focuses on growth.
  • You use it to choose the right algorithm and data structure when data can get large.

Real-world idea: how does effort grow with size? Big O helps you compare and choose algorithms.

Technical definition

  • Big O describes upper bound on growth (worst case): O(1) constant, O(n) linear, O(n²) quadratic, O(log n) logarithmic, O(n log n) for efficient sorts.
  • Drop constants and lower terms: 3n + 2 → O(n). Space complexity = extra memory used (e.g. O(n) for copying array).
  • Common: array access O(1); linear scan O(n); binary search O(log n); two nested loops over n O(n²); merge sort O(n log n).

Simple practical example

Drop constants and lower terms: 3n + 2 → O(n).

Code example

// Single loop → O(n)
for (let i = 0; i < n; i++) ...

// Two nested loops over n → O(n²)
for (let i = 0; i < n; i++)
  for (let j = 0; j < n; j++) ...

Why it matters

You must state time and space complexity and compare solutions. "What's the time complexity?" is asked in almost every problem.

Interview tip

"Big O = worst-case growth. State time and space. Know O(1), O(n), O(log n), O(n²), O(n log n) and when they appear."

Array

Simple real-life example (Understand first — very important for understanding)

  • Like a row of boxes numbered 0, 1, 2… You can jump to any box by index, add at the end, or walk through the whole row.
  • Arrays are good when you need order and fast access by position; inserting or deleting in the middle can be costly (you may shift elements).
  • The basic structure behind lists and vectors in most languages.

Real-world idea: ordered, indexable list. Arrays give fast access by index and are the foundation of many other structures.

Technical definition

  • Contiguous memory; index access O(1). Insert/delete at end O(1) amortized; in middle O(n) (shift). Size fixed (static) or dynamic (e.g. JS array, ArrayList).
  • Use for: random access, iteration, when order matters. Two pointers (start/end) for pair sum, palindrome; prefix sum for range sum.
  • Sorting in place: O(n log n) with quicksort/mergesort. Binary search on sorted array: O(log n).

Simple practical example

Use for: random access, iteration, when order matters.

Code example

// Two sum: use hash map for O(n)
// Or sort + two pointers if need to avoid duplicates
const twoSum = (arr, target) => {
  const seen = new Map();
  for (let i = 0; i < arr.length; i++) {
    if (seen.has(target - arr[i])) return [seen.get(target - arr[i]), i];
    seen.set(arr[i], i);
  }
};

Why it matters

Arrays are the most common input. Many problems are "array + two pointers" or "array + hash map". Know index vs value and in-place tricks.

Interview tip

"Array: O(1) access; insert middle O(n). Use two pointers, hash map, or sort + binary search depending on problem."

String (common operations)

Simple real-life example (Understand first — very important for understanding)

  • Like a sequence of characters (letters, digits, symbols). Often treated as an array of characters or as an immutable value.
  • Common operations: length, concatenate, find substring, split, replace. Many problems (parsing, matching) are about processing strings.
  • Pattern matching and string algorithms (e.g. sliding window) show up often in interviews.

Real-world idea: text as a sequence of characters. String operations and patterns are fundamental in many problems.

Technical definition

  • String as array of characters (in many languages immutable — operations return new string). Length, index, slice, compare. Check anagram: sort both or count chars (array/hash).
  • Palindrome: two pointers from both ends, or reverse and compare. Substring: sliding window or two pointers for "longest substring with at most K distinct".
  • Pattern matching: brute force O(n*m); KMP for linear; or hash (rolling hash) for Rabin-Karp. For interviews, often hash or brute is enough.

Simple practical example

Palindrome: two pointers from both ends, or reverse and compare.

Code example

// Palindrome check
const isPal = (s) => {
  let i = 0, j = s.length - 1;
  while (i < j) { if (s[i++] !== s[j--]) return false; }
  return true;
};

Why it matters

String problems are frequent (palindrome, anagram, substring, parsing). Know immutability and when to use array of chars for in-place style.

Interview tip

"Strings often immutable; use two pointers for palindrome/substring. Anagram: sort or count array. Sliding window for longest substring."

Linked List

Simple real-life example (Understand first — very important for understanding)

  • Like a chain of beads: each bead points to the next (and maybe the previous); there is no "index"—you walk from the head to find a position.
  • Adding or removing at the head (or tail with a tail pointer) is cheap; access by index is slow because you have to traverse.
  • Use when you insert/delete often at the beginning or when you don't need random access.

Real-world idea: items chained one after another. Linked lists are good when you add or remove at the ends often.

Technical definition

  • Nodes with value and pointer(s) to next (singly) or next + prev (doubly). No random access — traverse from head: O(n). Insert/delete at head O(1); at known node O(1) if you have ref; middle O(n) to find.
  • Use for: when insert/delete at head or in middle (with ref) is frequent; queues (with tail pointer); LRU cache (doubly + hash).
  • Classic: reverse list (iterative with prev/curr/next), detect cycle (slow/fast pointers), find middle (slow/fast), merge two sorted lists.

Simple practical example

Use for: when insert/delete at head or in middle (with ref) is frequent; queues (with tail pointer); LRU cache (doubly + hash).

Code example

// Reverse singly linked list
let prev = null, curr = head;
while (curr) {
  const next = curr.next;
  curr.next = prev;
  prev = curr;
  curr = next;
}
return prev;

Why it matters

"Reverse a linked list" and "detect cycle" are standard. Know dummy node trick and fast/slow pointers.

Interview tip

"No random access; insert at head O(1). Use dummy node for simpler edge cases. Fast/slow for cycle and middle."

Stack

Simple real-life example (Understand first — very important for understanding)

  • Like a stack of plates: you add on top (push) and take from the top (pop); last in, first out (LIFO).
  • Use for undo (push each state, pop to undo), matching brackets, or any "reverse order" or "last thing first" need.
  • No random access; you only see or remove the top.

Real-world idea: last in, first out. Stacks are for undo, parsing, and anything that needs "most recent first."

Technical definition

  • LIFO: push (add top), pop (remove top), peek (top without remove). All O(1) with array or linked list. Use for: undo, parentheses matching, DFS, expression evaluation (postfix).
  • Monotonic stack: keep stack in sorted order (e.g. strictly increasing). Use for "next greater element", "largest rectangle in histogram".
  • Implement with array (push/pop) or linked list. Call stack in recursion is a stack.

Simple practical example

Monotonic stack: keep stack in sorted order (e.

Code example

// Valid parentheses
const valid = (s) => {
  const stack = [], map = { ')': '(', ']': '[', '}': '{' };
  for (const c of s) {
    if (c in map) { if (stack.pop() !== map[c]) return false; }
    else stack.push(c);
  }
  return stack.length === 0;
};

Why it matters

Parentheses, valid parentheses, next greater element, and DFS all use stack. Monotonic stack is a common advanced pattern.

Interview tip

"Stack: LIFO, push/pop O(1). Use for matching, DFS, undo. Monotonic stack for next greater / histogram problems."

Queue

Simple real-life example (Understand first — very important for understanding)

  • Like a line at a counter: first in, first out (FIFO); you add at the back (enqueue) and take from the front (dequeue).
  • Use for tasks in order (e.g. print queue), BFS in graphs, or any fair ordering where the first arrived is served first.
  • Often implemented with a linked list or a circular array.

Real-world idea: first in, first out. Queues are for fair ordering and breadth-first processing.

Technical definition

  • FIFO: enqueue (add back), dequeue (remove front). O(1) with doubly linked list or circular buffer; array dequeue is O(n) if shift. Use for: BFS, task scheduling, sliding window max (deque).
  • Priority queue (heap): dequeue returns min or max. Use for merge K sorted lists, top K, Dijkstra. Deque (double-ended): push/pop both ends — sliding window max.
  • BFS on graph/tree always uses a queue to process level by level.

Simple practical example

Priority queue (heap): dequeue returns min or max.

Code example

// BFS template
const q = [start];
while (q.length) {
  const node = q.shift();
  // process node
  for (const next of node.neighbors) q.push(next);
}

Why it matters

BFS = queue. "Level order traversal" and "shortest path in unweighted graph" need a queue. Know when to use deque or priority queue.

Interview tip

"Queue: FIFO; use for BFS. Deque for sliding window max; priority queue (heap) for top K or Dijkstra."

Hash Table

Simple real-life example (Understand first — very important for understanding)

  • Like a dictionary: you look up a word (key) and get the definition (value); you can add, find, and remove by key very fast on average.
  • Hash tables map keys to values using a hash function; average lookup/insert/delete is O(1); good for "do I have this?" or "what value goes with this key?"
  • Use when you need fast lookup by key, counting occurrences, or deduplication.

Real-world idea: look up by key quickly. Hash tables give fast lookup, insert, and delete by key.

Technical definition

  • Key–value store: average O(1) insert, lookup, delete. Implemented with array + hash function + collision handling (chaining or open addressing).
  • Use for: count frequency, "have we seen this?", two sum, grouping (group anagrams by sorted key), cache (LRU with hash + doubly linked list).
  • Trade-off: no order (use ordered map if you need sorted keys). Collisions can degrade to O(n) worst case; good hash + load factor matter.

Simple practical example

Use for: count frequency, "have we seen this?

Code example

// Count frequency
const freq = new Map();
for (const x of arr) freq.set(x, (freq.get(x) || 0) + 1);
// Group anagrams: key = sorted string, value = list of strings

Why it matters

Many problems are "one pass with hash map": two sum, subarray sum, first non-repeating. "Can we do it in O(n)?" often means hash.

Interview tip

"Hash: O(1) average lookup. Use for frequency, 'seen', two sum, grouping. No order; use Map for key-count or key-list."

Tree & Binary Tree

Simple real-life example (Understand first — very important for understanding)

  • Like a family tree or folder structure: each node has children (and one parent); you can have a root and walk down the branches.
  • Trees are used for hierarchies, sorting (binary search tree), and many algorithms (traversals, path finding).
  • Common: binary tree (at most two children), and balanced trees for efficient search and insert.

Real-world idea: hierarchy and branching. Trees organize data in levels and support efficient search and traversal.

Technical definition

  • Tree: nodes, one root, each node has children. Binary tree: at most two children (left, right). No order guarantee unless BST. Traversals: inorder (L root R), preorder (root L R), postorder (L R root), level order (BFS).
  • Recursive definition: empty or root + left subtree + right subtree. Many problems: max depth, same tree, invert, path sum — solve with recursion (or iterative with stack/queue).
  • DFS: stack (or recursion); BFS: queue. Inorder on BST gives sorted order.

Simple practical example

Recursive definition: empty or root + left subtree + right subtree.

Code example

// Max depth
const maxDepth = (root) => !root ? 0 : 1 + Math.max(maxDepth(root.left), maxDepth(root.right));

// Inorder (BST → sorted)
const inorder = (root) => {
  if (!root) return;
  inorder(root.left);
  console.log(root.val);
  inorder(root.right);
};

Why it matters

Tree problems are everywhere: depth, path sum, serialize, LCA. Know the four traversals and when to use recursion vs iterative.

Interview tip

"Tree: recursive or stack/queue. Inorder/pre/post/level order. Inorder on BST = sorted. Many problems are recursive on left/right."

Binary Search Tree (BST)

Simple real-life example (Understand first — very important for understanding)

  • Like a sorted list arranged as a tree: smaller values go left, larger go right, so finding, inserting, and checking membership is fast when the tree is balanced.
  • Binary search tree (BST) keeps an ordering; in-order traversal gives sorted order. Balance (e.g. AVL, red-black) keeps operations O(log n).
  • Use when you need ordered data with fast search, insert, and delete.

Real-world idea: a tree that keeps things sorted. BST gives ordered storage with efficient search and updates.

Technical definition

  • Binary tree with order: left < root < right (or equivalent). Inorder traversal = sorted. Search, insert, delete: O(h) where h = height (O(log n) if balanced).
  • Use for: search, range queries (inorder between L and R), successor/predecessor. Validate BST: pass [min, max] down and check each node.
  • Balanced BST (AVL, Red-Black): h = O(log n). Language libraries: TreeSet/TreeMap (Java), set/map with order (C++), or sorted structures.

Simple practical example

Use for: search, range queries (inorder between L and R), successor/predecessor.

Code example

// Search in BST
const search = (root, val) => {
  if (!root || root.val === val) return root;
  return val < root.val ? search(root.left, val) : search(root.right, val);
};

Why it matters

"Validate BST", "Kth smallest in BST" (inorder), "LCA in BST" (compare with root) are common. Know inorder = sorted.

Interview tip

"BST: left < root < right; inorder = sorted. Search/insert O(h). Validate by passing [min,max] range."

Heap

Simple real-life example (Understand first — very important for understanding)

  • Like a priority queue: the "most important" (e.g. largest or smallest) is always at the top; you add items and always take or peek at the top.
  • A heap is a tree (usually binary) where the parent is always ≥ or ≤ children; insert and remove-top are O(log n).
  • Use for priority queues, heapsort, or "always process the biggest/smallest first."

Real-world idea: always get the top priority item. Heaps give fast insert and remove-max (or min).

Technical definition

  • Min-heap: parent ≤ children; root = min. Max-heap: parent ≥ children; root = max. Stored as array: parent at i, children at 2i+1, 2i+2. Insert and extract-min/max: O(log n).
  • Use for: priority queue, top K elements (min-heap of size K), merge K sorted lists, heap sort. Get min/max: O(1).
  • No general search: heap is not sorted; only guarantee is root. For "top K", keep K elements in heap; if larger than min, pop min and push new.

Simple practical example

Use for: priority queue, top K elements (min-heap of size K), merge K sorted lists, heap sort.

Code example

// Top K largest: min-heap of size K
// For each x: if heap.size < K, push; else if x > heap.peek(), pop then push(x)
// JS: no built-in heap; use array + manual heapify or library

Why it matters

"Top K frequent", "merge K sorted lists", "find median" (two heaps) use heaps. Know array representation and O(log n) insert/extract.

Interview tip

"Heap: min/max at root; insert/extract O(log n). Use for top K, merge K lists, median. Array: children at 2i+1, 2i+2."

Graph (BFS, DFS)

Simple real-life example (Understand first — very important for understanding)

  • Like a map of cities and roads: nodes (cities) and edges (roads); you can have one-way or two-way links, and weights (e.g. distance).
  • Graphs model networks: social connections, routes, dependencies. You traverse with BFS or DFS and solve path and connectivity problems.
  • Representation: adjacency list (list of neighbors per node) or matrix; choice affects speed and memory.

Real-world idea: things connected by links. Graphs model relationships and networks; BFS/DFS are the basic tools.

Technical definition

  • Graph: vertices + edges. Directed/undirected; weighted/unweighted. Representation: adjacency list (list of neighbors per node) or matrix. List is usually better for sparse graphs.
  • BFS: queue; shortest path in unweighted graph; level by level. DFS: stack or recursion; cycle detection, topological sort, path finding. Visit set to avoid revisiting.
  • Topological sort: DFS with finish order (reverse) or Kahn (in-degree, queue). Shortest path weighted: Dijkstra (priority queue) or Bellman-Ford.

Simple practical example

BFS: queue; shortest path in unweighted graph; level by level.

Code example

// DFS template
const visit = (node, seen) => {
  if (seen.has(node)) return;
  seen.add(node);
  for (const next of node.neighbors) visit(next, seen);
};
// BFS: queue + seen; push neighbors

Why it matters

"Number of islands", "clone graph", "course schedule" = graph + BFS/DFS. Know adjacency list and when BFS vs DFS.

Interview tip

"Graph: adjacency list. BFS = queue (shortest unweighted); DFS = stack/recursion (cycles, topo). Use visited set."

Trie

Simple real-life example (Understand first — very important for understanding)

  • Like a tree of letters: each path spells a word or prefix; you can find all words that start with a prefix or check if a word exists.
  • Tries are used for autocomplete, spell check, and prefix search; they trade space for fast prefix and lookup.
  • Each node can have many children (one per possible next character).

Real-world idea: store and search by prefix. Tries are for words and prefix-based lookup.

Technical definition

  • Trie (prefix tree): tree where each node represents a character; path from root = prefix. Use for: autocomplete, prefix search, "word in dictionary", word search II.
  • Insert: follow path character by character; create nodes as needed. Search: follow path; check if node is end-of-word. Space: O(total chars); time per op O(length of word).
  • Can store count or end-of-word flag at nodes. Good when many strings share prefixes.

Simple practical example

Insert: follow path character by character; create nodes as needed.

Code example

// Trie node: { children: Map<char, Node>, isEnd: boolean }
// insert(word): for each char, go to or create child; mark isEnd at last
// search(word): follow path; return isEnd at last node

Why it matters

"Implement autocomplete", "prefix search", "word break" (trie or DP). Less common than array/hash/tree but good to know.

Interview tip

"Trie: prefix tree; each node = char. Insert/search O(word length). Use for prefix search, autocomplete, word break."

Recursion

Simple real-life example (Understand first — very important for understanding)

  • Like a task that breaks into smaller versions of the same task: "solve for n by solving for n-1 and combining."
  • You need a base case (when to stop) and a recursive case (call itself with a smaller input).
  • Use for trees, divide-and-conquer, and when the structure of the problem is self-similar.

Real-world idea: solve by breaking into smaller same-shaped problems. Recursion is natural for trees and divide-and-conquer.

Technical definition

  • Function calls itself; base case (termination) and recursive case. Call stack holds state. Tree/graph DFS, divide-and-conquer (merge sort), backtracking are recursive.
  • Tail recursion: last action is recursive call (some languages optimize to loop). Avoid stack overflow: ensure base case is reached; consider iterative if depth is large.
  • Memoization: cache results of (params) to avoid repeated work — turns recursive solution into DP.

Simple practical example

Tail recursion: last action is recursive call (some languages optimize to loop).

Code example

// Fibonacci with memo
const fib = (n, memo = {}) => {
  if (n <= 1) return n;
  if (memo[n] != null) return memo[n];
  memo[n] = fib(n - 1, memo) + fib(n - 2, memo);
  return memo[n];
};

Why it matters

Many problems are naturally recursive (tree, backtracking). "Convert to iterative" or "add memoization" are common follow-ups.

Interview tip

"Base case + recursive case. Memoization to avoid repeated work (DP). Iterative or stack if depth too large."

Two pointers & sliding window

Simple real-life example (Understand first — very important for understanding)

  • Like two fingers on a list: one at the start, one at the end (or both at the start), moving in a rule-based way to find a pair or a range.
  • Often used on sorted arrays: move the left or right pointer depending on whether the current sum is too big or too small.
  • Saves time by avoiding nested loops when one pass with two pointers is enough.

Real-world idea: two positions moving through the data. Two pointers often give O(n) solutions for pair or range problems.

Technical definition

  • Two pointers: two indices (often start/end or slow/fast). Sorted array: move left or right by comparison (two sum, pair with sum). Linked list: slow/fast for cycle, middle.
  • Sliding window: subarray/substring with some invariant (e.g. sum ≤ K, at most K distinct chars). Expand end; if invalid, shrink start. O(n) for one pass.
  • Fixed-size window: just maintain window of size K and update result. Variable size: expand until invalid, then shrink.

Simple practical example

Sliding window: subarray/substring with some invariant (e.

Code example

// Two sum (sorted): left=0, right=n-1; if sum < target left++; else right--
// Sliding window: max subarray sum ≤ K: expand right, while sum > K subtract arr[start++]

Why it matters

"Two sum", "three sum", "longest substring with K distinct", "max subarray sum" — two pointers or sliding window are standard.

Interview tip

"Two pointers: sorted array or slow/fast. Sliding window: expand/shrink for subarray/substring; O(n) single pass."

Sorting (Quick, Merge)

Simple real-life example (Understand first — very important for understanding)

  • Like arranging cards in order: you compare and swap (or merge, or partition) until everything is in the right order.
  • Different algorithms (quick sort, merge sort, etc.) have different trade-offs: time, space, stability. You often use the language's built-in sort.
  • Understanding sorting helps with "find the kth largest" or "merge sorted lists" type problems.

Real-world idea: put items in order. Sorting is a building block for many other algorithms and interview problems.

Technical definition

  • Merge sort: divide into halves, sort each, merge. O(n log n) time, O(n) extra space. Stable. Good for linked list (no random access).
  • Quick sort: pick pivot (e.g. last); partition (smaller left, larger right); recurse on both. O(n log n) average, O(n²) worst; in-place. Not stable.
  • Use built-in sort (usually O(n log n)) unless asked to implement. Know one O(n log n) sort and when stability or in-place matters.

Simple practical example

Quick sort: pick pivot (e.

Code example

// Merge sort: merge(left, right) with two pointers
// Quick sort: partition(arr, lo, hi) returns pivot index; then sort(lo, p-1), sort(p+1, hi)

Why it matters

"Sort an array" or "merge two sorted" are building blocks. "Implement quicksort/mergesort" sometimes asked. Know time/space and stability.

Interview tip

"Merge: O(n log n), stable, O(n) space. Quick: O(n log n) avg, in-place, pivot + partition. Built-in sort unless implement required."

Dynamic programming

Simple real-life example (Understand first — very important for understanding)

  • Like solving a puzzle by filling a table: you break the problem into subproblems, solve each once, store the answer, and reuse it.
  • DP avoids recomputing the same subproblem (e.g. Fibonacci, shortest path); you need a recurrence and a base case, then iterate or recurse with memoization.
  • Use when the problem has overlapping subproblems and optimal substructure.

Real-world idea: solve subproblems once and reuse. Dynamic programming turns exponential work into polynomial by storing results.

Technical definition

  • Solve by breaking into overlapping subproblems; store results (memoization or table) to avoid recomputation. Often "optimal substructure": optimal solution uses optimal solutions of subproblems.
  • Steps: define state (e.g. dp[i] = best for prefix i), recurrence (how dp[i] depends on earlier dp), base case, order of fill (often left to right or by dependency).
  • Classic: Fibonacci, climb stairs, coin change, knapsack, LCS, LIS, edit distance, max subarray. 1D or 2D table; sometimes space-optimized (e.g. only previous row).

Simple practical example

Steps: define state (e.

Code example

// Climb stairs: dp[i] = dp[i-1] + dp[i-2], dp[0]=1, dp[1]=1
// Coin change: dp[amount] = min over coins of 1 + dp[amount - coin]

Why it matters

DP is a major interview topic. "Can you do it in O(n²)?" often means DP. Start with recursive + memo, then convert to table if asked.

Interview tip

"Define state and recurrence; base case; fill table (or memo). Start recursive + memo; optimize space if needed."

Greedy

Simple real-life example (Understand first — very important for understanding)

  • Like making the best local choice at each step: pick the largest coin, schedule the shortest job first, and hope the overall result is optimal.
  • Greedy doesn't always give the best global answer, but when it does (e.g. activity selection, Huffman), it's simple and fast.
  • You often prove that the greedy choice is safe (stays in an optimal solution) and that subproblems remain.

Real-world idea: take the best local step and hope for the best overall. Greedy works when local best leads to global best.

Technical definition

  • Greedy: make locally optimal choice at each step hoping for global optimum. Works when "greedy choice" is safe (e.g. sort by end time for interval scheduling).
  • No general rule — prove or intuition that greedy works. Common: sort then iterate (intervals, tasks), Huffman, activity selection, jump game, merge intervals.
  • When greedy fails: need to consider future (e.g. some knapsack). Then use DP or backtracking.

Simple practical example

No general rule — prove or intuition that greedy works.

Code example

// Merge intervals: sort by start, then if curr.start <= last.end, merge (last.end = max(last.end, curr.end)); else push new

Why it matters

"Merge intervals", "non-overlapping intervals", "jump game" are often greedy. Know when to try "sort + one pass" or "pick best local option."

Interview tip

"Greedy: local optimal choice. Often sort first (by end or start). Prove it works for the problem or give counterexample."

Common patterns

Simple real-life example (Understand first — very important for understanding)

  • Many problems reuse the same ideas: sliding window (contiguous subarray), fast/slow pointers (cycle detection), or merge intervals.
  • Recognizing the pattern (e.g. "this is a BFS," "this is two pointers") helps you pick the right structure and algorithm.
  • Practice common patterns so you can map a new problem to a known approach.

Real-world idea: reuse known patterns. Sliding window, two pointers, and other patterns show up again and again.

Technical definition

  • Prefix sum: precompute sum[0..i]; range sum [i,j] = sum[j] - sum[i-1]. O(1) range sum after O(n) precompute. Use for subarray sum, equilibrium.
  • Sliding window: see section 15. Two pointers: sorted array or slow/fast. Binary search on answer: range [lo, hi], check(mid) feasible.
  • Cycle detection: Floyd (slow/fast) in linked list or graph. Union Find: disjoint sets, merge and find parent; use for connectivity, cycle in undirected graph.

Simple practical example

Sliding window: see section 15.

Code example

// Prefix sum: sum[i] = sum[i-1] + arr[i]; rangeSum(i,j) = sum[j] - (sum[i-1] || 0)
// Union Find: find(x) with path compression; union(x,y) by rank or size

Why it matters

Many problems are a combination: "subarray sum" = prefix sum or sliding window; "connectivity" = Union Find or BFS/DFS.

Interview tip

"Prefix sum for range sum. Union Find for disjoint sets. Know sliding window, two pointers, binary search on answer; combine as needed."

More Design & Architecture notes

OOP · Microservices · Project Architecture · System Design · All developer notes