Unit 1 · Algorithms · Lesson 1 of 24
Complexity analysis
Step 1: Learn the ideas
- Asymptotic notation: O, Theta, Omega and tight bounds
- Word-RAM cost model and where it breaks (big integers, long keys, caches)
- Worst-case, expected (randomized) and average-case bounds
- Amortized analysis: aggregate, accounting and potential methods
- Auxiliary space versus input and output space
- Recursion depth as space
- Multiple input dimensions: O(n·m), O(V + E), O(N log k)
- Output-sensitive bounds for enumeration problems
- Recurrences: recursion trees and the master theorem
- Using input limits to pick a target complexity
Why it matters
Before writing code in an interview you should already know roughly how fast it will run and whether that is fast enough for the input limits. Complexity analysis gives you that prediction, lets you compare two designs without implementing both, and is the first thing an interviewer checks when you propose an approach. The same reasoning tells you in production whether a job survives ten times more data, or whether a "constant-time" lookup hides a linear scan.
Core ideas
Notation. O is an upper bound, Ω a lower bound, Θ both. In interviews "O" is usually read as a tight bound, so do not say O(n log n) if your code is O(n). Constants and lower-order terms are dropped, but they still decide between two solutions of the same class.
Which case you are stating.
- Worst case holds for every input of size n: binary search, merge sort, heap operations.
- Expected averages over the algorithm's own random choices, for every input: randomized quickselect, hashing with a randomly chosen hash function. It is not the same as average case, which averages over an assumed input distribution.
- Amortized bounds the total cost of any sequence of m operations, divided by m. A dynamic array that doubles its capacity copies 1 + 2 + 4 + ... < 2n elements over n appends, so append is amortized O(1), although the single append that triggers growth costs O(n). Amortized bounds are worst-case statements about sequences; no probability is involved.
Time versus space. Report auxiliary space (what you allocate beyond the input) and say whether the output counts. Recursion consumes stack: DFS on a path-shaped tree with n nodes is n frames deep even if it allocates nothing else, while merge sort recurses only O(log n) deep.
Several input dimensions. Do not collapse independent sizes into one n. A grid search is O(rows × cols); BFS over an adjacency list is O(V + E); merging k sorted lists of N total elements with a heap is O(N log k); comparing all pairs of n words of length up to L is O(n² × L). Hashing a string key costs O(L), not O(1).
Output-sensitive bounds. When the answer is large, the bound must include it. There are 2ⁿ subsets with average size n/2, so listing them is Θ(n × 2ⁿ) for any algorithm: no method can be faster than writing its output.
Recurrences. Draw the recursion tree and sum work per level. T(n) = 2T(n/2) + O(n) has log n levels of O(n) work: O(n log n). T(n) = T(n/2) + O(1) is O(log n). T(n) = T(n − 1) + O(n) sums to O(n²), which is why a recursion that passes a copied slice a[1:] is quadratic.
From limits to a target. A rough rule of thumb is around 10⁸ simple operations per second in compiled languages and roughly an order of magnitude fewer in CPython. So n near 10⁵ to 10⁶ calls for O(n log n) or better, n near 10³ to 10⁴ allows O(n²), and n up to about 20 allows O(2ⁿ × n). Use this as a sanity check, not a law.
Common misconceptions
- "Hash map operations are O(1)." They are expected O(1) under good hashing and a bounded load factor, plus the cost of hashing the key. Adversarial or degenerate keys make them O(n), and a resize makes an individual insert expensive.
- "Amortized means average over random inputs." It is a guarantee for every sequence of operations; individual operations may still be slow, which matters for latency-sensitive code.
- "Recursion costs no space." Every active call holds a frame. Depth-n recursion is O(n) space and can exceed a language's stack limit.
- "Two nested loops mean O(n²)." Count total iterations instead. A sliding window whose left index only moves forward is O(n) despite its inner loop.
- "Slicing and string concatenation are free." In most languages a slice copies its elements, and repeatedly concatenating immutable strings can be quadratic.
- "The asymptotically better algorithm is always faster." Asymptotics describe growth. At realistic sizes, constants, allocation and cache behavior can reverse the ranking, which is why measurement still matters.
How to approach it
State the complexity unprompted, once when you propose an approach and again after coding, defining every variable ("n words of maximum length L"). Name the case: worst, expected or amortized. Include auxiliary space and recursion depth, and price every library call you use (sorting, slicing, membership tests on lists). At every level interviewers check the bound of your own code; at mid and senior levels they also ask how it changes when an assumption changes: keys become long strings, inputs become adversarial, or data no longer fits in memory.
Suggested order
- 1Read MIT 6.006 Recitation 1 (with Lecture 1: Introduction) for asymptotic notation and the model of computation, then do its asymptotics exercises.
- 2Read the amortized-analysis part of MIT 6.006 Lecture 2 (Data Structures) and reproduce the doubling argument for dynamic arrays on paper.
- 3Read the recurrences section of MIT 6.006 Recitation 3 and solve T(n) = 2T(n/2) + n, T(n) = T(n/2) + 1 and T(n) = T(n − 1) + n with recursion trees.
- 4For deep-level preparation, read Jeff Erickson’s lecture notes on amortized analysis (binary counter) to see the accounting and potential methods once.
- 5Open the official cost table for your interview language (for Python, the documentation page on time complexity of built-in types) and annotate ten operations you use daily.
- 6Solve Implement Queue using Stacks and explain its amortized bound, then Design HashMap and state exactly what its O(1) assumes.
- 7For Kth Largest Element in an Array, Merge k Sorted Lists, Subsets and Number of Islands, write the full bound with every dimension before coding.
Step 2: Check off what you can do
Tap each one you can genuinely do or explain. Each earns 10 XP.
Step 3: Go deeper
MIT 6.006 Introduction to Algorithms (Spring 2020)Start here
MIT OpenCourseWareSection: Recitation 1
Asymptotic notation, the word-RAM model of computation and short exercises on comparing growth rates; read with Lecture 1.
Open(opens in a new tab)Link responded, 5 Oct 2026MIT 6.006 Introduction to Algorithms (Spring 2020)
MIT OpenCourseWareSection: Lecture 2: Data Structures
The amortized analysis of dynamic arrays: why append is constant time over any sequence of operations.
Open(opens in a new tab)Link responded, 5 Oct 2026MIT 6.006 Introduction to Algorithms (Spring 2020)
MIT OpenCourseWareSection: Recitation 3
Solving recurrences with substitution, recursion trees and the master theorem, with exercises.
Open(opens in a new tab)Link responded, 5 Oct 2026Algorithms (Jeff Erickson): Recursion and Backtracking chapters, with amortized-analysis notes
Jeff Erickson, University of Illinois Urbana-ChampaignSection: Lecture notes: Amortized Analysis
Aggregate, accounting and potential-method arguments worked through on a binary counter, for deep-level understanding.
Open(opens in a new tab)Link responded, 5 Oct 2026Python documentation: costs of built-in types and the container modules
Python Software Foundation (docs.python.org)Section: Time complexity of operations on built-in types
A concrete official cost table that shows amortized, average-case and worst-case caveats for real library operations.
Open(opens in a new tab)Link responded, 5 Oct 2026
Step 4: Practice
Treat these as complexity exercises as much as coding exercises. Before coding, write the time and auxiliary-space bound with every input dimension and say whether it is worst-case, expected or amortized; after coding, check the bound against the code you actually wrote, including library calls and recursion depth. Contains Duplicate and Two Sum contrast sorting with hashing, Implement Queue using Stacks is the amortized example, Kth Largest contrasts expected and worst-case time, and Merge k Sorted Lists, Subsets and Number of Islands need multi-dimensional or output-sensitive bounds. Afterwards, keep the habit by stating the full bound before solving any problem from the Top Interview 150 plan.
- Contains DuplicateLeetCode(opens in a new tab)Not attemptedLeetCode
- Two SumLeetCode(opens in a new tab)Not attemptedLeetCode
- Implement Queue using StacksLeetCode(opens in a new tab)Not attemptedLeetCode
- Design HashMapLeetCode(opens in a new tab)Not attemptedLeetCode
- Kth Largest Element in an ArrayLeetCode(opens in a new tab)Not attemptedLeetCode
- Merge k Sorted ListsLeetCode(opens in a new tab)Not attemptedLeetCode
- SubsetsLeetCode(opens in a new tab)Not attemptedLeetCode
- Number of IslandsLeetCode(opens in a new tab)Not attemptedLeetCode
- Top Interview 150 (official study plan)LeetCode(opens in a new tab)Not attemptedLeetCode
Record attempts on each problem’s page (+15 XP, +10 more when solved on your own).
Step 5: Test yourself
Answer each one out loud or on paper before you finish the lesson.
- Question 1What is the difference between expected O(1) and amortized O(1)?
- Question 2What is the space complexity of recursive DFS on a path-shaped tree with n nodes?
- Question 3Why is listing all subsets of n items at least n · 2^n work for any algorithm?
- Question 4What does average O(1) hash lookup assume, and what does hashing a string of length L cost?
- Question 5Why is a recursion that passes a[1:] at each call quadratic?
- Question 6With n up to 2 · 10^5, which complexity classes are realistic targets?
You’re done when you can
- State correct time and auxiliary-space bounds, including recursion depth, for five unseen solutions in about a minute each.
- Explain without notes why dynamic-array append is amortized O(1) and why a single append can be O(n).
- Solve three standard recurrences with recursion trees and name an algorithm that matches each.
- Give one example each of a worst-case, an expected and an amortized guarantee, and say what assumption each depends on.
Finish the lesson
Unlocks next: Arrays, strings and hashing, Linked lists, Stacks, queues and deques, Heaps, selection and streaming, Recursion and backtracking, Bit manipulation


