Big O describes how an algorithm's time or memory grows as its input grows, ignoring constant factors. From slowest growth to fastest, the classes you meet in interviews are O(1), O(log n), O(n), O(n log n), O(n²), O(2ⁿ) and O(n!). This sheet lists what each structure and sort costs, and how to say it out loud.
Common complexities, ranked
The last column is what the class means at a modest input of 1,000 items. The gap between the rows is why a better class beats a faster machine.
| Class | Name | Plain example | Steps at n = 1,000 |
|---|---|---|---|
| O(1) | Constant | Read an array element by index | 1 |
| O(log n) | Logarithmic | Binary search: each step halves what is left | About 10 |
| O(n) | Linear | Scan a list once to find its largest value | 1,000 |
| O(n log n) | Linearithmic | Merge sort, or sort first and then make one pass | About 10,000 |
| O(n²) | Quadratic | Compare every pair with two nested loops | 1,000,000 |
| O(2ⁿ) | Exponential | Try every subset of the input | A number with 302 digits |
| O(n!) | Factorial | Try every ordering of the input | A number with 2,568 digits |
What the input size tells you
Read this table backward before you pick an approach. It assumes about 100 million simple steps in about a second, which is a rule of thumb and not a law, but it tells you which classes are worth trying. If n can reach 100,000, an O(n²) answer is about 10 billion steps, and saying so before you write anything is a strong signal.
| Largest n | Class that fits | What that usually means |
|---|---|---|
| About 11 | O(n!) | Every ordering, with backtracking |
| About 25 | O(2ⁿ) | Every subset, or bitmask dynamic programming |
| About 500 | O(n³) | Three nested loops, or dynamic programming over ranges |
| About 10,000 | O(n²) | Two nested loops, or a 2-D table |
| About 5,000,000 | O(n log n) | A sort, a heap, or divide and conquer |
| About 100,000,000 | O(n) | One pass, two pointers, a hash map |
| Larger | O(log n) or O(1) | Binary search, or a formula |
Reading the cost from your own code
You rarely need to recall a figure. Most answers come from a few rules applied to what you just wrote.
| What you see | What it costs | Example |
|---|---|---|
| Steps one after another | Add them, keep the largest | A sort, then a loop: O(n log n + n), which is O(n log n) |
| One loop inside another | Multiply them | Nested loops over one list: O(n²) |
| Loops over two different inputs | Keep both variables | O(m · n), never O(n²) |
| A range halved each step | O(log n) | Binary search |
| An index that only moves forward | O(n) in total, however nested | Sliding window |
| Recursion | Calls times work per call | Merge sort: O(n) work on each of log n levels |
| Two recursive calls per step, depth n | O(2ⁿ) unless cached | Plain recursion on overlapping subproblems |
| Recursion depth | Counts as space | Recursive tree search: O(h) for height h |
def has_close_pair(nums, gap):
"""True if two values differ by at most gap."""
ordered = sorted(nums) # O(n log n) time, O(n) for the copy
for i in range(len(ordered) - 1): # n - 1 steps
if ordered[i + 1] - ordered[i] <= gap: # O(1) each
return True
return False # total: O(n log n) time, O(n) space
def longest_run_of_ones(bits):
"""Two loops, but j never revisits an index, so the total is O(n)."""
best = i = 0
while i < len(bits):
j = i
while j < len(bits) and bits[j] == 1:
j += 1
best = max(best, j - i)
i = max(j, i + 1) # i jumps past what j has walked
return bestThe first function is the sum rule: the sort dominates the pass. The second is the trap in the other direction. It has a loop inside a loop and is still linear, because every index is walked once. Saying why is worth more than the answer itself, and it is the question interviewers ask next.
Data structure operations
These are the costs of the structures themselves. A language's built-in can differ or attach conditions, so for those use the language pages below. In the table, h is the height of a tree and m is the length of a key.
| Structure | Access by position | Search | Insert | Delete | Space |
|---|---|---|---|---|---|
| Array, fixed size | O(1) | O(n) | O(n), by shifting | O(n), by shifting | O(n) |
| Dynamic array | O(1) | O(n) | O(1) amortized at the end, O(n) elsewhere | O(1) at the end, O(n) elsewhere | O(n) |
| Linked list | O(n) | O(n) | O(1) at a node you hold | O(1) at a node you hold | O(n) |
| Stack | O(1), top only | O(n) | O(1), push | O(1), pop | O(n) |
| Queue | O(1), front only | O(n) | O(1), enqueue | O(1), dequeue | O(n) |
| Hash map | n/a | O(1) average, O(n) worst | O(1) average, O(n) worst | O(1) average, O(n) worst | O(n) |
| Hash set | n/a | O(1) average, O(n) worst | O(1) average, O(n) worst | O(1) average, O(n) worst | O(n) |
| BST, balanced | n/a | O(log n) | O(log n) | O(log n) | O(n) |
| BST, unbalanced | n/a | O(h), which is O(n) at worst | O(h), which is O(n) at worst | O(h), which is O(n) at worst | O(n) |
| Binary heap | O(1), top only | O(n) | O(log n) | O(log n), top only | O(n) |
| Trie | n/a | O(m) | O(m) | O(m) | O(total characters) at worst |
A graph has its own operations, and the choice of representation changes all of them. V is the number of nodes and E the number of edges.
| Representation | Space | Check one edge | List a node's neighbors | Add an edge | Remove an edge |
|---|---|---|---|---|---|
| Adjacency list | O(V + E) | O(degree) | O(degree) | O(1) | O(degree) |
| Adjacency matrix | O(V²) | O(1) | O(V) | O(1) | O(1) |
Most interview graphs have far fewer edges than V², so an adjacency list is the default. Reach for a matrix when the graph is dense or the question keeps asking whether one specific edge exists. The graph patterns guide covers what to do once it is built.
Sorting algorithms
You will rarely write one of these in an interview, but you will often be asked what your language's sort costs, or why a counting sort beats O(n log n) here. A stable sort keeps equal items in their original order, which matters when you sort by one key and then another.
| Algorithm | Best | Average | Worst | Extra space | Stable |
|---|---|---|---|---|---|
| Merge sort | O(n log n) | O(n log n) | O(n log n) | O(n) | Yes |
| Quicksort | O(n log n) | O(n log n) | O(n²) | O(log n) stack on average | No |
| Heapsort | O(n log n) | O(n log n) | O(n log n) | O(1) | No |
| Insertion sort | O(n) | O(n²) | O(n²) | O(1) | Yes |
| Selection sort | O(n²) | O(n²) | O(n²) | O(1) | No |
| Bubble sort, with an early exit | O(n) | O(n²) | O(n²) | O(1) | Yes |
| Counting sort, values up to k | O(n + k) | O(n + k) | O(n + k) | O(n + k) | Yes |
| Radix sort, d digits in base b | O(d · (n + b)) | O(d · (n + b)) | O(d · (n + b)) | O(n + b) | Yes |
No sort based only on comparing pairs can beat O(n log n) in the worst case. Counting and radix sort get around that by reading the values themselves, which is why they need a limited range of keys. When an interviewer asks whether you can do better than O(n log n), this is usually the answer they are checking for.
How to state complexity out loud
A figure with no reasoning sounds like a guess, even when it is right. A strong answer has four parts, and takes about fifteen seconds:
- Give both costs, time and then space, without waiting to be asked.
- Derive them from your code, naming the part that dominates.
- Name your variables, especially when there are two inputs.
- Qualify the figure: average case, amortized, or not counting the output.
The sort is O(n log n), and the loop after it is O(n) with a constant-time lookup inside, so the sort dominates and the total is O(n log n) time. Space is O(n) for the map, not counting the output. That lookup is O(1) on average rather than in the worst case, so a lot of colliding keys would make it slower. I don't think we can beat O(n log n) with this approach, because the order is what makes the single pass work.
| The word | What it means | How it sounds |
|---|---|---|
| Worst case | A limit on any single input | "Worst case O(n), if every key collides." |
| Average case | The usual cost, under an assumption you can state | "O(1) on average, assuming the hash spreads keys well." |
| Amortized | The cost per operation over a long run, where one operation may cost more | "Append is O(1) amortized: the occasional resize is spread across many appends." |
| Extra space | Memory beyond the input and the output | "O(1) extra, not counting the list I return." |
This sheet is the lookup table. For what interviewers actually ask and how to handle the follow-ups, read time and space complexity in interviews. To hear how yours sounds, a coding mock interview with Josh, Devana's AI interviewer, asks about your solution as you write it, and the report scores logic explanation and code defense from what you say.
What your language's built-ins cost
Each of these pages takes its figures from that language's own documentation, cites it, and leaves out what the documentation does not state.
- Python: list, dict, set, deque, heapq and str. Python publishes a full table, so this is the most complete of the four.
- JavaScript: Map, Set, Array, Object and strings. The specification promises very little, and that page says exactly what, and how to reason without it.
- Java: ArrayList, LinkedList, HashMap, TreeMap, HashSet, PriorityQueue and ArrayDeque, with the conditions the Javadoc attaches.
- C++: vector, unordered_map, map, set, priority_queue and deque, with average and worst cases.
Common mistakes
- Calling a hash lookup O(1) with no qualifier. It is O(1) on average, and an interviewer may ask about the worst case.
- Writing O(n²) for two loops over different inputs. It is O(m · n), and the difference can decide whether the approach is fast enough.
- Forgetting the call stack. A recursive solution with no other memory still uses space proportional to its depth.
- Treating a slice or a string concatenation as free. In Python, slicing a list is O(j - i), so a slice inside a loop can quietly make a solution quadratic.
- Claiming a nested loop is O(n²) on sight. If the inner index never goes back, the total can be O(n), so count the steps instead of the loops.
Frequently asked questions
What is the time complexity of a hash map?
Lookup, insert and delete are O(1) on average and O(n) in the worst case, when many keys collide. Space is O(n). The average assumes a hash function that spreads keys well, so say on average when you state it, because the interviewer may ask about the worst case.
What does amortized O(1) mean?
That the cost per operation is constant when averaged over a long run, even though a single operation may cost more. Appending to a dynamic array is the usual example: most appends are cheap, and the occasional resize copies everything, but its cost is spread across all the appends that came before it.
Do I need to memorize a Big O cheat sheet for interviews?
Memorize only the core data structure operations and the fact that good comparison sorts are O(n log n). Almost everything else you can derive from your own code by adding steps in sequence and multiplying nested work. Interviewers listen for that derivation more than for the figure.