Coding Interviews

The Coding Interview Cheat Sheet: Complexity, Patterns and Python Idioms

A coding interview cheat sheet worth keeping open: complexity budgets, data structure costs, pattern triggers, Python idioms and a pre-submit checklist.

The Stealth Interview Team8 min read
The Coding Interview Cheat Sheet: Complexity, Patterns and Python Idioms

This coding interview cheat sheet is the reference sheet I would want open during preparation: the complexity budget implied by each input size, what every operation actually costs in Python, the phrases that give away which pattern a problem wants, the standard library calls worth knowing by heart, and a short checklist to run before you claim a solution works.

It is a lookup table, not a tutorial. If a row makes no sense, that is the topic to go and study.

How to use this coding interview cheat sheet#

Read it in the order an interview happens: constraints, then pattern, then data structure, then code, then check. The most valuable table is the first one, because the bound on n narrows the possible solutions before you have understood the problem.

Complexity budgets by input size#

A judge or an interviewer's mental model allows roughly 10^8 simple operations. Working backwards from that gives a rule of thumb — not a law, but a good first filter.

Bound on nComplexity that fitsTypical technique
n <= 12O(n!)Permutation search, brute-force ordering
n <= 20O(2^n)Subset enumeration, bitmask DP
n <= 100O(n^4)Four nested loops, interval DP on small inputs
n <= 500O(n^3)Floyd–Warshall, triple loops
n <= 5,000O(n^2)Pairwise DP, quadratic scans
n <= 10^5O(n log n)Sorting, heap, binary search, divide and conquer
n <= 10^6O(n)Single pass, hash map, sliding window
n <= 10^9O(log n) or O(sqrt n)Binary search on the answer, number theory

Two corollaries worth saying out loud in an interview. If n is at most twenty and the answer is an arrangement, the intended solution is exponential and you should stop looking for a clever polynomial one. If n is 10^9, no algorithm may touch every element, so the answer is a formula or a search over the answer space.

What operations cost in Python#

Average-case, with the ones that actually catch people in bold.

StructureOperationCost
listindex, append, pop from endO(1)
listinsert at 0, pop from 0O(n)
listx in listO(n)
listsort()O(n log n)
dict / setget, set, in, deleteO(1) average
dequeappend and pop at either endO(1)
dequeindex the middleO(n)
heapqpush, popO(log n)
heapqheapify a listO(n)
strbuild by += in a loopO(n^2)
strbuild with "".join(parts)O(n)
bisectsearch a sorted listO(log n)
bisectinsort into a listO(n) — the shift, not the search

The two that quietly ruin submissions are list.pop(0) inside a BFS — use a deque — and string concatenation in a loop, which is quadratic because each += copies the whole string.

Pattern triggers#

What the statement says, and what it usually means. The full version with templates is in the pattern map.

Phrase in the problemReach for
"contiguous subarray", "substring"Sliding window, prefix sum
"sorted array", "find a pair"Two pointers
"minimum largest", "maximum smallest", "at most k per…"Binary search on the answer
"next greater", "first taller", "span"Monotonic stack
"prerequisites", "build order", "can it be finished"Topological sort
"minimum number of steps", unweightedBFS
"connected", "groups merge as edges arrive"Union-find
"top k", "kth largest", "median of a stream"Heap
"how many ways", "minimum cost to reach"Dynamic programming
"all subsets", "all permutations", "place n items"Backtracking
"starts with", "autocomplete", "dictionary of words"Trie
"appears once", "appears twice except one"Bit manipulation

When two patterns fit, name both and pick the one whose correctness you can argue. An interviewer scores the argument, not the guess.

If a row is unfamiliar, its canonical problem is the fastest way in: Two Sum for hash maps, Longest Substring Without Repeating Characters for windows, Koko Eating Bananas for binary search on the answer, and Number of Islands for grids.

Python idioms worth knowing by heart#

The imports:

Python
from collections import Counter, defaultdict, deque
from heapq import heappush, heappop, heapify, nlargest
from bisect import bisect_left, bisect_right
from functools import cache
from math import inf, gcd

Counting, grouping and frequencies:

Python
counts = Counter("mississippi")
top_two = counts.most_common(2)

groups = defaultdict(list)
for word in ["eat", "tea", "tan"]:
    groups["".join(sorted(word))].append(word)

anagram_key = tuple(sorted(Counter("listen").items()))

Heaps, including the max-heap workaround:

Python
def two_largest(nums):
    heap = []                      # min-heap capped at 2; its top is the smaller of the pair
    for value in nums:
        heappush(heap, value)
        if len(heap) > 2:
            heappop(heap)
    return sorted(heap, reverse=True)

def largest_first(nums):
    heap = [-value for value in nums]
    heapify(heap)
    return -heappop(heap)

Binary search over a sorted list, without writing the loop:

Python
def count_in_range(sorted_nums, low, high):
    """How many values fall in [low, high], inclusive."""
    return bisect_right(sorted_nums, high) - bisect_left(sorted_nums, low)

Memoised recursion, which is how every dynamic programming solution should start:

Python
def longest_common_subsequence(a, b):
    @cache
    def best(i, j):
        if i == len(a) or j == len(b):
            return 0
        if a[i] == b[j]:
            return 1 + best(i + 1, j + 1)
        return max(best(i + 1, j), best(i, j + 1))

    return best(0, 0)

Sorting by a compound key — the tuple is the whole trick:

Python
people = [("ada", 36), ("alan", 41), ("grace", 36)]
people.sort(key=lambda person: (person[1], person[0]))
descending_then_ascending = sorted(people, key=lambda p: (-p[1], p[0]))

And the grid bug that costs people a whole interview:

Python
wrong = [[0] * 3] * 4        # four references to the SAME row
right = [[0] * 3 for _ in range(4)]
wrong[0][0] = 1              # every row now starts with 1
right[0][0] = 1              # only the first row changes

If you write a deep recursion on a large grid, raise the limit before it bites:

Python
from sys import setrecursionlimit

setrecursionlimit(10 ** 6)

Edge cases to test, every time#

Run the list, not your intuition. Most of these take five seconds to check and each one has ended somebody's interview.

  • Empty input. [], "", a null root, zero nodes.
  • One element. Especially with two pointers, windows and midpoints.
  • Two elements. The smallest case where "left" and "right" differ.
  • All elements identical. Breaks strict comparisons and de-duplication.
  • Already sorted, and reverse sorted. The best and worst case for anything with a partition.
  • Negatives and zero. Windows over sums silently break with negatives.
  • k larger than n, or k equal to zero.
  • Duplicates in the input when the answer is a set of indices.
  • Disconnected or cyclic graphs, and nodes with no edges.
  • Integer division and rounding. Python's // floors toward negative infinity; int() truncates toward zero. They differ on negative numbers.

The thirty-second check before you say "done"#

  1. Return type. Index or value? The list, or its length? What do you return on failure — -1, None, or an empty list?
  2. Loop bounds. Does the last element get processed? Is range exclusive where you meant inclusive?
  3. Empty input. Does it return rather than crash?
  4. Mutation. Did you modify the list you are iterating over, or mutate the caller's input without saying so?
  5. State reset. Anything initialised outside a loop that should have been inside it, or vice versa?
  6. Complexity, out loud. Time and space, with one sentence of justification each.

Then walk one non-trivial example through by hand, out loud, and say which case you are checking. Interviewers score this explicitly, and it is the only reliable way to find your own off-by-one before someone else does.

What to say, and when#

PhaseMinutesWhat you say
Clarify2–4Input types, bound on n, sorted or not, degenerate inputs, return value
Approach3–6The brute force, its complexity, the target complexity, the pattern
Code15–20Low-rate narration: what this block does, not every line
Test5–8One normal case, two edge cases, traced by hand
Follow-ups5Complexity, what changes at scale, what you would do differently

The single most common failure is spending twenty-five minutes coding and none testing, then finding a bug at minute forty with no time to fix it. Testing is not the part you cut when you are behind.

Where to go from here#

If a row above was unfamiliar, that is the topic. The pattern map has a template and three practice problems for each technique; the Blind 75 is the shortest list that covers most of them, with a six-week plan if you want a schedule; and the full problem index is filterable by pattern and difficulty when you want more of one specific thing.

And if you want a second pair of eyes during the interview itself, Stealth Interview is a desktop app for macOS and Windows that reads the problem from a screenshot and walks through the approach, the code and the complexity with you, while staying invisible to screen sharing.

Frequently asked questions

How do I work out the expected complexity from the constraints?
Assume the judge allows roughly 10^8 simple operations. Divide that budget by the bound on n and see what shape fits: n up to 10^6 leaves room for a linear pass, n up to 10^5 leaves room for O(n log n), n up to 5,000 leaves room for O(n²), and n up to 20 with an exponential answer means backtracking or a bitmask. In an interview the constraint is often unstated — ask for it, because it is the cheapest hint you will ever get.
Which Python built-ins are worth memorising for interviews?
Counter and defaultdict from collections, deque for O(1) operations at both ends, heappush and heappop from heapq, bisect_left for binary search over a sorted list, and lru_cache or functools.cache for memoised recursion. Together they remove most of the boilerplate from a timed problem. Knowing that sorted takes a key function, and that tuples compare element by element, covers most of the rest.
What should I check before I say I am done?
Five things, in about thirty seconds: the return type is what was asked for (index versus value, list versus count), the empty input does not crash, the loop bounds include the last element, the input was not mutated while being iterated, and you can state time and space complexity with a reason. That pass catches most of what interviewers otherwise catch for you.
Is it bad to use library functions in a coding interview?
Almost never, unless the library function is the problem. Using sorted, Counter or heapq shows you know your standard library, which is a real signal. Calling a built-in that trivially solves the stated task — a regex for a parsing question, or a library's own binary search when binary search is what is being tested — is worth asking about first: say what you would use and ask whether they want you to implement it.
How much of an interview should be spent talking rather than typing?
Roughly the first eight minutes and the last eight, with the middle spent typing while narrating at a low rate. Clarify inputs and constraints, state a brute force and a target complexity, and only then write. At the end, walk one example through by hand and state the complexity. Silence in the middle is fine; silence at either end is what loses interviews.

Keep reading

Ace your next coding interview

Stealth Interview is a desktop app for macOS and Windows that reads the problem off your screen and answers with a working solution, a step-by-step explanation and its time and space complexity — while staying invisible to screen sharing.

Get Stealth Interview