Every question to ask, 29 patterns, 40 algorithms and the data structures, with code that is compiled and tested in C++ and Python.
14 pages
C++ + Python
3 themes
New videos, system design breakdowns and the AI news worth your time. The TEACH cheat sheet free when you join. Unsubscribe anytime.
All 14 pages
Printable, and the same sheet as a web page you can come back to. Shown in your theme.
1 / 14 The TEACH method
2 / 14 Patterns, continued
3 / 14 Patterns, the rest
4 / 14 Dynamic programming is a family
5 / 14 Data structures at a glance
6 / 14 Big O at a glance
7 / 14 Big O: sorting, graphs and traps
8 / 14 Language quick reference
9 / 14 Test cases
10 / 14 TEACH, step by step
11 / 14 Algorithms at a glance
12 / 14 Algorithms, continued
13 / 14 Algorithms, continued
14 / 14 Algorithms, continued
Free code, tested
Three full snippets so you can judge the quality before you sign up. Each one is compiled and run against its tests, in both languages.
BFS
O(V + E) · O(V)
Fewest steps when every step costs the same.
Show codeC++ and Python
// LC 994 · BFS, multi-source shortest steps on a grid
// O(V + E) time, O(V) space (here V = m * n)
#include <queue>
#include <utility>
#include <vector>
using namespace std;
int orangesRotting(vector<vector<int>> g) { // by value: we mark cells in place
int m = g.size(), n = g[0].size(), fresh = 0, t = 0;
queue<pair<int, int>> q;
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++) {
if (g[i][j] == 2) q.push({i, j});
else if (g[i][j] == 1) fresh++;
}
int d[] = {0, 1, 0, -1, 0};
for (; !q.empty() && fresh; t++) // one level per minute
for (int s = q.size(); s--; ) {
auto [x, y] = q.front(); q.pop();
for (int k = 0; k < 4; k++) {
int a = x + d[k], b = y + d[k + 1];
if (a < 0 || b < 0 || a >= m || b >= n || g[a][b] != 1) continue;
g[a][b] = 2; fresh--; // mark seen when pushing
q.push({a, b});
}
}
return fresh ? -1 : t;
}
# LC 994 · BFS, multi-source shortest steps on a grid
# O(V + E) time, O(V) space (here V = m * n)
from collections import deque
def oranges_rotting(grid):
g = [row[:] for row in grid] # copy: we mark cells in place
m, n = len(g), len(g[0])
q = deque((i, j) for i in range(m) for j in range(n) if g[i][j] == 2)
fresh = sum(row.count(1) for row in g)
t = 0
while q and fresh:
for _ in range(len(q)): # one level per minute
x, y = q.popleft()
for a, b in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
if 0 <= a < m and 0 <= b < n and g[a][b] == 1:
g[a][b] = 2 # mark seen when pushing
fresh -= 1
q.append((a, b))
t += 1
return -1 if fresh else t
C++ compiled with clang (C++17) and run against 305 tests. Python 3.14 run against 305.
Binary search
O(log n) · O(1), times the cost of ok()
Sorted input, or a yes or no check that flips once.
Show codeC++ and Python
// LC 704 · 34 · 1011 · Binary search, half-open [lo, hi)
// O(log n) time, O(1) space, times the cost of ok()
#include <algorithm>
#include <numeric>
#include <vector>
using namespace std;
int lowerBound(const vector<int>& a, int t) { // first i with a[i] >= t (LC 704: then check i < n && a[i] == t)
int lo = 0, hi = a.size();
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (a[mid] >= t) hi = mid; else lo = mid + 1;
}
return lo;
}
int upperBound(const vector<int>& a, int t) { // first i with a[i] > t (LC 34: [lower, upper - 1] if lower < upper)
int lo = 0, hi = a.size();
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (a[mid] > t) hi = mid; else lo = mid + 1;
}
return lo;
}
int shipWithinDays(const vector<int>& w, int days) { // LC 1011: smallest feasible capacity
int lo = *max_element(w.begin(), w.end());
int hi = accumulate(w.begin(), w.end(), 0) + 1;
while (lo < hi) {
int mid = lo + (hi - lo) / 2, need = 1, load = 0;
for (int x : w) {
if (load + x > mid) need++, load = 0;
load += x;
}
if (need <= days) hi = mid; else lo = mid + 1; // ok(mid)
}
return lo;
}
# LC 704 · 34 · 1011 · Binary search, half-open [lo, hi)
# O(log n) time, O(1) space, times the cost of ok()
def lower_bound(a, t): # first i with a[i] >= t (LC 704: then check i < n and a[i] == t)
lo, hi = 0, len(a)
while lo < hi:
mid = lo + (hi - lo) // 2
if a[mid] >= t:
hi = mid
else:
lo = mid + 1
return lo
def upper_bound(a, t): # first i with a[i] > t (LC 34: [lower, upper - 1] if lower < upper)
lo, hi = 0, len(a)
while lo < hi:
mid = lo + (hi - lo) // 2
if a[mid] > t:
hi = mid
else:
lo = mid + 1
return lo
def ship_within_days(w, days): # LC 1011: smallest feasible capacity
lo, hi = max(w), sum(w) + 1
while lo < hi:
mid = lo + (hi - lo) // 2
need, load = 1, 0
for x in w:
if load + x > mid:
need, load = need + 1, 0
load += x
if need <= days: # ok(mid)
hi = mid
else:
lo = mid + 1
return lo
C++ compiled with clang (C++17) and run against 514 tests. Python 3.14 run against 513.
Hash map and set
You keep asking “have I seen this?” or need counts.
Show codeC++ and Python
// Hash map and set: unordered_map, unordered_set
#include <optional>
#include <unordered_map>
#include <unordered_set>
#include <vector>
using namespace std;
unordered_map<int, int> countFreq(const vector<int>& a) {
unordered_map<int, int> cnt;
for (int x : a) cnt[x]++; // get + put: O(1) avg per key
return cnt; // worst case, all keys collide: O(n) per op
}
bool hasKey(const unordered_map<int, int>& m, int k) { return m.count(k) > 0; } // contains: O(1) avg
void removeKey(unordered_map<int, int>& m, int k) { m.erase(k); } // delete: O(1) avg
int sumValues(const unordered_map<int, int>& m) { // iterate: O(n)
int s = 0;
for (auto& [k, v] : m) s += v;
return s;
}
optional<int> firstRepeat(const vector<int>& a) { // "have I seen this?", nullopt if none
unordered_set<int> seen;
for (int x : a)
if (!seen.insert(x).second) return x; // insert fails if present: O(1) avg
return nullopt;
}
# Hash map and set: dict, Counter, set
from collections import Counter, defaultdict
def count_freq(a):
return Counter(a) # counts in one pass: O(n), each put O(1) avg
def group_by_parity(a):
groups = defaultdict(list) # missing key starts as []
for x in a:
groups[x % 2].append(x) # get + put: O(1) avg
return groups
def has_key(m, k): return k in m # contains: O(1) avg
def remove_key(m, k): m.pop(k, None) # delete: O(1) avg
def sum_values(m): return sum(m.values()) # iterate: O(n)
# worst case, all keys collide: O(n) per operation
def first_repeat(a): # "have I seen this?", None if none
seen = set()
for x in a:
if x in seen: return x # contains: O(1) avg
seen.add(x) # put: O(1) avg
return None
C++ compiled with clang (C++17) and run against 13 tests. Python 3.14 run against 13.
Patterns that move
Eight of the 29 patterns, each with a short loop. The other 21 and all the code are in the sheet.
Two pointers
Sorted input, a pair, or matching ends.
Fast and slow pointers
A cycle in a linked list, or its middle.
Sliding window
The longest or shortest run under a rule.
Binary search on a boundary
Sorted, or where a yes or no check flips.
Cyclic sort
Values 1 to n. Find missing or doubled.
In-place list reversal
Reverse a linked list, or a stretch of it.
Stack and monotonic stack
Brackets, or the next greater element.
Monotonic deque
The max or min of every sliding window.
Big-O at a glance
Drag the input size to see which costs survive. The data structure, sorting, graph and cost-trap tables are in the full sheet.
Drag n from 1 to a billion. The bars show how many operations each cost class needs. The verdict assumes about 100,000,000 simple operations per second in C++, 10,000,000 in Python, and a limit of about a second.
O(1)1instant
O(log n)17instant
O(n)100,000instant
O(n log n)1.7 × 10⁶fine
O(n²)1.0 × 10¹⁰too slow
O(2ⁿ)more than 10¹⁰⁰too slow
O(n!)more than 10¹⁰⁰too slow
At n = 100,000 in C++, aim for O(n log n) or better.
Data structures
Average case unless a column says worst. Space is O(n) in the number of stored items; a trie is O(total characters).
Structure
Access
Search
Insert
Delete
Worst case
Array
O(1)excellent
O(n)good
O(n)good
O(n)good
same
Dynamic array (append)
O(1)excellent
O(n)good
O(1) amortizedexcellent
O(n) middle, O(1) at the endgood
append O(n) when it grows
Linked list
O(n)good
O(n)good
O(1) at a known nodeexcellent
O(1) at a known node, doubly linked listexcellent
same
Stack, queue, deque
O(n) linked, O(1) array-backedgood
O(n)good
O(1)excellent
O(1)excellent
same
Hash map, hash set
n/a
O(1)excellent
O(1)excellent
O(1)excellent
O(n) when every key collides, or on a rehashbad
Binary search tree
O(log n)excellent
O(log n)excellent
O(log n)excellent
O(log n)excellent
O(n) when skewedbad
Balanced tree (AVL, red-black)
O(log n)excellent
O(log n)excellent
O(log n)excellent
O(log n)excellent
O(log n)excellent
Binary heap
peek O(1)excellent
O(n)good
O(log n)excellent
pop min O(log n)excellent
push and pop O(log n); build from n items O(n)good
Trie (key length L)
n/a
O(L)excellent
O(L)excellent
O(L)excellent
space O(total characters)
Union-find
n/a
find O(α(n)) amortizedexcellent
union O(α(n)) amortizedexcellent
n/a
α(n) is below 5 for any real n
Data structures at a glance
What each one is for. The costs of every move are in the sheet.
Array
You need items by position, or one pass in order.
String
Text. Count letters with a 26-slot array.
Hash map and set
You keep asking “have I seen this?” or need counts.
Stack
The newest item matters first. Brackets, undo, next greater.
Queue and deque
First in, first out. BFS, or a window you slide.
Linked list
You splice nodes in and out. LRU cache: a hash map plus a doubly linked list.
Heap
You keep needing the smallest or the largest.
Binary tree and BST
Hierarchy, or sorted order that changes as you go.
Trie
Prefix search and autocomplete over many words.
Graph, adjacency list
Things connect to things. Keep each node’s neighbors.
Union-find
Groups merge as edges arrive, and you ask “same group?”
Ordered map and set
Keys in sorted order, or the nearest key above or below.
40 algorithms
Time and space, and the trigger that tells you to reach for each one. Templates and tested code are in the sheet.
Sorting and selection
Merge sort
O(n log n) · O(n) extra on arrays, O(1) on a linked list with bottom-up merging · stable