Master Complexity Cheatsheet
Complexity is the one thing you’re expected to know cold, without looking it up mid-interview or mid-contest. This page is the single-place reference: every operation, data structure, and algorithm from this track, with its time and space complexity side by side. Treat it as a lookup table, not a lesson — if a row doesn’t make sense, the phase that taught it is linked from context in earlier pages.
What you’ll learn
Section titled “What you’ll learn”- The real complexity of every common
list/dict/set/deque/heapqoperation — including the ones that silently cost . - Every sorting and searching algorithm from Phase 4, side by side.
- Every data structure’s core operations, from arrays to segment trees, Fenwick trees, and DSU.
- The graph algorithm family — BFS, DFS, Dijkstra, Bellman-Ford, Floyd-Warshall, MST, and topological sort.
- The Big-O growth hierarchy, and a rule of thumb for how large
ncan be under a typical time limit.
Common Python operations
Section titled “Common Python operations”| Operation | list | dict | set | deque | heapq |
|---|---|---|---|---|---|
Index / key access x[i] | avg | — | — | ||
| Append / add | amortized | avg | avg | (heappush) | |
| Append/insert at front | — | — | — | ||
| Pop from end | — | — | — | ||
| Pop from front | — | — | (heappop, smallest only) | ||
| Insert / delete at arbitrary index | avg (del d[k]) | avg | not supported directly | ||
Membership test x in ... | avg | avg | |||
| Search for min/max | (min only, heap[0]) | ||||
Sort (sorted(...), .sort()) | on items | on items | (heapify + drain) | ||
sorted(list) from scratch | — | — | — | — | Building via heapify is |
Sorting algorithms
Section titled “Sorting algorithms”| Algorithm | Best | Average | Worst | Space | Stable? | Notes |
|---|---|---|---|---|---|---|
Timsort (Python’s sorted/.sort()) | Yes | Adaptive — exploits existing runs of order | ||||
| Merge Sort | Yes | Guaranteed , no worst-case cliff | ||||
| Quick Sort | No | Worst case needs an adversarial/sorted input plus a bad pivot rule | ||||
| Heap Sort | No | In-place, but poor cache locality in practice | ||||
| Insertion Sort | Yes | Best-in-class for nearly-sorted or tiny arrays | ||||
| Selection Sort | No | Always comparisons, minimal writes | ||||
| Bubble Sort | Yes | Teaching tool only — never the right production choice | ||||
| Counting Sort | Yes | k = range of key values; useless when k >> n | ||||
| Radix Sort | Yes | d = number of digits/passes | ||||
| Bucket Sort | Yes (with a stable inner sort) | Needs a roughly-uniform input distribution |
Searching
Section titled “Searching”| Technique | Time | Space | Requires |
|---|---|---|---|
| Linear search | Nothing | ||
| Binary search (exact match) | Sorted array | ||
lower_bound / upper_bound (bisect) | Sorted array | ||
| Binary search on the answer | Depends on feasible() | Monotonic feasibility | |
Hash-based lookup (dict/set) | average, worst | Hashable keys | |
| Two pointers / sliding window | Sorted or monotonic structure |
Core data structures
Section titled “Core data structures”| Structure | Access | Search | Insert | Delete | Space | Notes |
|---|---|---|---|---|---|---|
Array / Python list | worst (end: amortized) | Contiguous, cache-friendly | ||||
| Singly/doubly linked list | at a known node | at a known node | No random access; cheap splicing | |||
Stack (list as stack) | (append) | (pop) | LIFO — both ops at the same end | |||
Queue (collections.deque) | (append) | (popleft) | FIFO — never use list.pop(0) here | |||
Hash table (dict/set) | — | avg, worst | avg | avg | Worst case needs adversarial hash collisions | |
Binary heap (heapq) | (min only) | (min) | heapify builds from scratch in | |||
| Binary Search Tree (unbalanced) | worst | worst | worst | worst | Degenerates to a linked list on sorted input | |
| Balanced BST (AVL / Red-Black) | Guarantees hold even on adversarial input | |||||
| Trie | L = length of the key/word | |||||
| Segment Tree | query/update | — | update | — | build; lazy propagation keeps range updates | |
| Fenwick Tree (BIT) | query | — | update | — | Simpler and faster in practice than a segment tree for prefix sums | |
| Disjoint Set Union (DSU) | — | find | union | — | With path compression + union by rank/size; is the inverse Ackermann function — effectively constant |
Graph algorithms
Section titled “Graph algorithms”| Algorithm | Time | Space | Answers |
|---|---|---|---|
| BFS | Shortest path in an unweighted graph, level order, multi-source distance | ||
| DFS | Reachability, cycle detection, topological order, connected components | ||
| Topological sort (Kahn’s or DFS-based) | A valid linear order of a DAG | ||
| Dijkstra (binary heap) | Shortest path, non-negative weights only | ||
| Bellman-Ford | Shortest path with negative weights; also detects negative cycles | ||
| Floyd-Warshall | All-pairs shortest paths, works with negative weights (no negative cycles) | ||
| Prim’s MST (binary heap) | Minimum spanning tree, dense-ish graphs | ||
| Kruskal’s MST (with DSU) | Minimum spanning tree, sparse graphs / edge lists |
The Big-O growth hierarchy
Section titled “The Big-O growth hierarchy”From fastest-growing-slowly to fastest-growing-explosively:
| Complexity | Name | Feels like |
|---|---|---|
| Constant | A dict lookup, an array index | |
| Logarithmic | Binary search, balanced BST operations | |
| Root | Trial-division primality check, sparse tables’ build-adjacent factor | |
| Linear | A single pass — linear search, array sum | |
| Linearithmic | Comparison-based sorting, most divide-and-conquer | |
| Quadratic | Nested loops over the same input — naive pair comparisons | |
| Cubic | Triple-nested loops — Floyd-Warshall, naive matrix multiply | |
| Exponential | Enumerating every subset, unmemoized exponential recursion | |
| Factorial | Enumerating every permutation |
Max n per time limit — a rule of thumb
Section titled “Max n per time limit — a rule of thumb”Contest judges typically allow roughly - simple operations per
second in Python (often with a looser limit than C++ for exactly this
reason). Working backward from a 1-2 second limit gives a rough ceiling
on n for each complexity class:
| Complexity | Comfortable max n |
|---|---|
| -ish | |
| -25 | |
| -500 | |
| -10,000 | |
| - | |
| - | |
| / | up to (the input size barely matters) |
Practice
Section titled “Practice”Self-check — reading a constraint. Given and a 1-second limit, complete the comparison that decides whether an solution fits comfortably.
How to use this cheatsheet
Section titled “How to use this cheatsheet”- Treat it as a lookup, not reading material — bookmark it and come back mid-problem when you need to double-check a complexity before committing to an approach.
- When a problem gives you
nand a time limit, use the “maxnper time limit” table to shortlist which complexity classes are even in the running before you design an algorithm. - When two data structures both “work,” pick the one whose row here has the better guarantee for the operation your problem calls the most (e.g. a Fenwick tree over a segment tree when you only need prefix sums, not range updates).
Read the constraint, name the intended complexity
Section titled “Read the constraint, name the intended complexity”Count the operations, not the vibes
Section titled “Count the operations, not the vibes”Complexity
Section titled “Complexity”The one table this whole page exists to support — what fits in about one second, at roughly simple operations:
Max n | Largest complexity that fits | Typical approach |
|---|---|---|
| <= 12 | permutations, brute-force TSP | |
| <= 25 | subsets, bitmask DP | |
| <= 500 | Floyd-Warshall, interval DP | |
| <= 5,000 | pairwise DP, LCS, edit distance | |
| <= 10^5-10^6 | sorting, heaps, segment trees, binary search on the answer | |
| <= 10^7 | one pass, sliding window, prefix sums | |
| >= 10^9 | / | binary search on the answer, closed form, digit DP |
Two adjustments that matter in practice:
- Python’s constant factor is roughly 10-100x C++. Shift the table down about one order of
magnitude for pure-Python loops — simple operations per second is a safer working figure than
. Work pushed into C (
sort,sum,bytes,setoperations, NumPy) does not pay that tax. - Read the bound’s variable. “Binary search on the answer” is where is the
value range, not
n; a pseudo-polynomial DP like knapsack is in the capacity. Quoting either as a function ofnalone is the usual mistake, and it is what makes a solution that looks fine time out.
Interview follow-ups
Section titled “Interview follow-ups”| They ask | What they’re checking | The answer |
|---|---|---|
“The constraint is n <= 20. What does that tell you?” | Reading constraints as hints | That exponential in n is expected — is about a million. A bound that small is almost always pointing at a bitmask or a full enumeration |
“What is the complexity of list.insert(0, x)?” | Whether you know the data model | — every element shifts. That is why a queue uses collections.deque, where both ends are |
“in on a list versus a set?” | The most common Python performance bug | versus average. A membership test inside a loop turns an algorithm into without changing a line of the logic |
| “Amortised versus worst case?” | Precision | A list.append is amortised and on the resize that copies. Over n appends the total is , so amortised is the honest figure to quote for a loop |
| “Your solution is but times out” | Diagnosing beyond the class | Either the constant is the problem — Python-level loops, per-iteration allocation, string concatenation in a loop — or the bound’s variable is not what you assumed. Check both before rewriting the algorithm |
| “Space complexity of your recursion?” | The invisible cost | stack frames, which is on a degenerate input and invisible in the source. It is also why CPython’s ~1000-frame limit turns some correct solutions into crashes |
| “Sorting is — can you ever beat it?” | Knowing the model | Only by leaving the comparison model: counting or radix sort is for small integer keys. The bound applies to comparison sorts specifically |
| “Which of these bounds do people quote wrongly?” | Judgement | Pseudo-polynomial ones — knapsack’s and digit DP’s — because they are polynomial in a value rather than an input length. Naive Fibonacci is , not |
LeetCode problem set
Section titled “LeetCode problem set”Problems whose intended solution is decided by a complexity budget rather than a trick. For each, work out the target complexity from the constraints before looking at the tags.
Work down the ladder. Tick each problem off as you go — progress is saved in this browser, and the Export button in the filter bar writes it to a file you can keep.
- 1Two Sumeasy
- 121Best Time to Buy and Sell Stockeasy
- 21Merge Two Sorted Listseasy
- 217Contains Duplicateeasy
- 35Search Insert Positioneasy
- 169Majority Elementeasy
- 704Binary Searcheasy
- 1046Last Stone Weighteasy
- 33Search in Rotated Sorted Arraymedium
- 49Group Anagramsmedium
- 75Sort Colorsmedium
- 347Top K Frequent Elementsmedium
- 621Task Schedulermedium
- 875Koko Eating Bananasmedium
- 34Find First and Last Position of Element in Sorted Arraymedium
- 36Valid Sudokumedium
- 189Rotate Arraymedium
- 215Kth Largest Element in an Arraymedium
- 274H-Indexmedium
- 763Partition Labelsmedium
- 846Hand of Straightsmedium
- 912Sort an Arraymedium
- 981Time Based Key-Value Storemedium
- 2013Detect Squaresmedium
- 23Merge k Sorted Listshard
- 135Candyhard
- 315Count of Smaller Numbers After Selfhard
- 493Reverse Pairshard
- 1851Minimum Interval to Include Each Queryhard
Recall card
Section titled “Recall card”- The budget — about simple operations per second, and an order of magnitude fewer in pure Python. Do this arithmetic before choosing an approach.
- Constraint to complexity —
n <= 12: ·<= 25: ·<= 500: ·<= 5000: ·<= 10^6: ·<= 10^7: · beyond: or a formula. - Python data model —
list.insert(0, ...)andlist.pop(0)are ;dequeis at both ends.x in listis ,x in setis — the most common accidental . appendis amortised, on the resize. Quote the amortised figure for a loop.- Recursion costs space and dies at ~1000 frames in CPython.
- Read the bound’s variable — and are polynomial in a value, not a length. That is what makes a “fine” solution time out.
- Beating requires leaving the comparison model: counting or radix sort.
- Python’s built-in containers hide real complexity costs —
list.pop(0)andlist.insert(0, x)are , not ; usedequewhen you need fast operations at both ends. - Timsort’s worst case and best case (on
already-sorted runs) is why
sorted()/.sort()is almost always the right call over a hand-rolled sort. - Balanced BSTs, Fenwick trees, and segment trees all trade a bit of extra bookkeeping for a guaranteed where a naive structure would degrade to on adversarial input.
- Shortest-path algorithm choice is driven entirely by what the graph allows: unweighted -> BFS, non-negative weights -> Dijkstra, negative weights -> Bellman-Ford, all-pairs on a small graph -> Floyd-Warshall.
- The Big-O hierarchy plus a rough “operations per second” budget is enough to sanity-check almost any algorithm choice before you write a line of code.
Next: Phase 10 — Interview & Contest Strategy, where these templates and complexity guarantees get applied under real time pressure: how to plan an approach, communicate it, and choose the right tool fast when the clock is running.
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading