E ExamMaster

Data Structures & Algorithms · Data Structures & Algorithms

Time and Space Complexity

Asymptotic analysis with Big O notation for time and space, including recurrence solving.

Eight concepts on asymptotic analysis — Big O / Ω / Θ, dropping constants, the common growth ladder, nested-loop counting, the Master Theorem, unrolling halving recurrences, amortized cost, and the worst / average / amortized split interviewers actually mean.

  • Data Structures & Algorithms
  • Medium level
  • 8 concepts
  • 5 practice questions

1Big O, Omega, and Theta

Big O is an asymptotic upper bound: f(n) = O(g(n)) means f grows no faster than a constant times g for large n. Omega (\Omega) is a lower bound — f grows at least as fast as g. Theta (\Theta) is a tight bound: both O and \Omega at once.

Interview and contest speech almost always means worst-case Big O. Saying "this is O(n^2)" does not claim the algorithm is slow on every input; it claims the cost never exceeds a quadratic envelope. A matching \Omega statement is a separate claim.

Figure. Big-O is an upper sandwich, Ω a lower one; Θ means both bounds match the same g up to constants.

Three bound words

  1. Big OUpper envelope — "\le a constant times g" for large n.
  2. OmegaLower envelope — "\ge a constant times g" for large n.
  3. ThetaTight — both envelopes, so f and g grow at the same rate.
Bound cheat sheet
NotationClaimInterview default
O(g)Upper boundYes — worst-case ceiling
\Omega(g)Lower boundRare unless asked
\Theta(g)Tight boundWhen you can prove both sides
A function that is both O(n^2) and \Omega(n^2) is
  1. O(n) only
  2. \Theta(n^2)
  3. \Omega(n^3)

Matching upper and lower bounds at n^2 is exactly the definition of \Theta(n^2).

2Drop constants and lower-order terms

Asymptotic class keeps only the fastest-growing term and ignores constant multipliers. So 3n^2 + 5n + 7 is O(n^2): the n^2 term dominates, and the factor 3 does not change the class.

The same rule turns O(n^2 + n) into O(n^2) and O(2n \log n) into O(n \log n). Constants matter for wall-clock time; they do not change which Big-O bucket the algorithm sits in.

Figure. At large n the n^2 shape dominates; the leading coefficient and the linear term do not change the Big-O class.

How to reduce

  1. Find dominantCompare degrees / growth classes; keep the fastest term.
  2. Drop constantsStrip coefficients in front of that term.
  3. Drop lowerDiscard every slower-growing addend.

Share of the quadratic term at n = 100

For f(n) = 3n^2 + 5n + 7 at n = 100, what fraction of f(n) is the 3n^2 term alone?

  • 3n^2 at n = 1003 \times 10000 = 30000
  • 5n + 7 at n = 100500 + 7 = 507
  • f(100) = 30000 + 50730507
  • 30000 / 30507\approx 0.983 (98.3%)

Pro tip. At n = 1000 the same share is 3000000 / 3005007 \approx 0.998. The lower terms vanish in the limit — that is why Big O drops them.

7n^3 + 100n^2 + 50 is
  1. O(n^2)
  2. O(n^3)
  3. O(n^4)

The cubic term dominates. O(n^4) is a valid but looser upper bound; the tight class after dropping constants is O(n^3) (in fact \Theta(n^3)).

3Common growth order

The standard ladder of common classes is O(1) < O(\log n) < O(n) < O(n \log n) < O(n^2) < O(2^n) < O(n!). Each step eventually dominates the previous one as n grows.

Interview answers live on this ladder. Nested loops often land on n^2; binary search and balanced trees land on \log n; merge sort and heap sort land on n \log n; naive subset enumeration lands on 2^n.

Figure. Representative sizes at n = 16 for 1, \log_2 n, n, n \log_2 n, and n^2. Not to scale with 2^n or n! — those need a log axis the vocabulary does not have.

Reading the ladder

  1. Polys firstConstant → log → linear → n \log n → quadratic are the everyday interview set.
  2. Then explode2^n and n! outgrow every polynomial; they need a different scale to plot honestly.
  3. Pick the className the tightest class from the ladder that still upper-bounds the cost.

Costs at n = 16

Evaluate representative sizes 1, \log_2 n, n, n \log_2 n, n^2, and 2^n at n = 16.

  • \log_2 164
  • n and n \log_2 n16 and 16 \times 4 = 64
  • n^2256
  • 2^{16}65536 (256\times the n^2 value)

Pro tip. The bars below stop at n^2 because 2^{16} is 256 times taller — a shared linear scale would squash every earlier class to a hairline.

Which is the correct ordering?
  1. O(n) < O(\log n) < O(n^2)
  2. O(\log n) < O(n) < O(n \log n) < O(n^2)
  3. O(n^2) < O(n \log n) < O(n)

Log grows slower than linear; n \log n sits between linear and quadratic.

4Nested loops multiply

Independent nested loops over the same n multiply their iteration counts: an outer n with an inner n is O(n^2). A loop that halves each step contributes an O(\log n) factor instead of a linear one.

A triangular pattern — inner loop runs to i while i runs 1..nstill lands in the quadratic class. The exact sum n(n+1)/2 collapses to O(n^2) after dropping the constant and the linear term; halving the triangle does not change the Big-O bucket.

Figure. Triangle of filled cells for n=5: exactly n(n+1)/2=15 iterations — still O(n^2) after dropping constants.

Count the iterations

  1. SumInner passes total 1 + 2 + \cdots + n.
  2. Closed formThat sum is n(n+1)/2.
  3. AsymptoticDrop the 1/2 and the +n/2O(n^2).

Triangular double loop

for i in range(1, n + 1):
    for j in range(1, i + 1):
        work()

Triangular loop for n = 5

Outer i runs 1..5; inner j runs 1..i. How many times does `work()` run, and what is the asymptotic class?

  • Inner counts1+2+3+4+5
  • n(n+1)/2 at n = 55 \times 6 / 2 = 15
  • Drop constants / lower termsO(n^2)

Pro tip. Halving the triangle does not change the Big-O class — n^2/2 is still O(n^2). Exact counts matter for timeouts; the class answers the interview.

Outer loop n times, inner loop n times (independent) is
  1. O(n)
  2. O(n \log n)
  3. O(n^2)

Independent nested linear loops multiply: n \times n = n^2.

5Master Theorem

For divide-and-conquer recurrences T(n) = aT(n/b) + f(n) with a \ge 1 and b > 1, compare f(n) to n^{\log_b a}. That comparison picks the asymptotic case without fully expanding the recursion tree.

Case 1: f is polynomially smaller than n^{\log_b a}T(n) = \Theta(n^{\log_b a}). Case 2: f matches that power (up to log factors in the fine print) → an extra \log n appears, as in merge sort. Case 3: f is polynomially larger and a regularity condition holds → T(n) = \Theta(f(n)).

Figure. Master Theorem compares f(n) to n^{\log_b a}: leaf work, balanced, or root-dominated — pick the matching case, not a mash-up.

Apply the theorem

  1. Read a, bCount subproblems a and the shrink factor b.
  2. Critical powerCompute n^{\log_b a} — the work if leaves dominated.
  3. Compare fDecide Case 1 / 2 / 3 from how f sits relative to that power.

Merge sort via Master Theorem

Solve T(n) = 2T(n/2) + O(n), the merge-sort recurrence.

  • a = 2, b = 2\log_b a = 1
  • n^{\log_b a}n^1 = n
  • f(n) = O(n) vs nsame order → Case 2
  • Case 2 resultT(n) = O(n \log n)

Pro tip. "Split into two halves, then do linear combine work" is the merge-sort shape — memorize O(n \log n) for that pattern, and re-derive it with a=b=2 when you need the justification.

For T(n) = 2T(n/2) + O(n), the Master Theorem critical power n^{\log_b a} is
  1. n
  2. n^2
  3. \log n

a=2, b=2\log_2 2 = 1n^1 = n, which matches the linear merge work (Case 2).

6Unrolling a halving recurrence

When the Master Theorem is overkill, unroll. Binary search pays O(1) per level and halves the size: T(n) = T(n/2) + O(1). After k steps the size is n/2^k; stop when that size is 1, so k = \log_2 n and T(n) = O(\log n).

The same unrolling on merge sort's tree — \log n levels, O(n) work per level — recovers O(n \log n) without naming cases. Prefer the theorem when the branching factors are awkward; prefer unrolling when the recursion tree is obvious.

Figure. Each step halves the argument: \log_2 n levels until size 1 — the classic binary-search / merge depth.

Unroll T(n) = T(n/2) + O(1)

  1. One stepPay O(1), leave size n/2.
  2. k stepsSize n/2^k. Halt at size 1 → k = \log_2 n.
  3. Totalk constant-time steps → O(\log n).

Halvings for n = 16

Starting from size 16, how many times can you halve before the size reaches 1?

  • 16 \to 81 step
  • 8 \to 4 \to 2 \to 13 more steps
  • Total halvings4 = \log_2 16

Pro tip. Ceilings on odd lengths are absorbed by Big O. Interviewers still like hearing \lceil \log_2 n \rceil as the iteration cap.

T(n) = T(n/2) + O(1) solves to
  1. O(n)
  2. O(\log n)
  3. O(n \log n)

Constant work per halving level and \Theta(\log n) levels. The n \log n shape needs linear work on every level.

7Amortized analysis

Amortized cost averages an operation over a long sequence. A dynamic-array append is O(1) amortized even though a resize is O(n) when it hits: across n appends from empty with doubling, the copy costs 1 + 2 + 4 + \cdots + n = O(n), so the average per append is O(1).

Amortized is not a synonym for average-case over random inputs. It is a worst-case bound on the total of a sequence, divided by the number of operations — a few calls pay a lot; most pay almost nothing; the average stays constant.

Figure. Resize copy counts for eight appends with doubling. The three growth steps sum to 7 — less than one full extra pass over the final length.

Why doubling amortizes

  1. Cheap appendIf size < capacity, one write — O(1).
  2. ResizeWhen full, copy into a ~2× block — O(\text{capacity}) once.
  3. AverageGeometric copies sum to O(n) over n appends → O(1) each amortized.

Eight appends from capacity 1

Start at capacity 1, size 0. Append eight elements, doubling on every full resize. How many element copies do resizes perform in total, and what is that per append?

  • Resize copies (before appends that need growth)1 + 2 + 4 = 7
  • Appends performed8
  • Copies per append = 7/80.875O(1) amortized

Pro tip. The worst single append is still O(n). Amortized answers "over many appends"; real-time code that cannot hitch still reserves capacity up front.

n appends into a doubling dynamic array cost, in total,
  1. O(n), so each append is O(1) amortized
  2. O(n^2), because every append copies the whole array
  3. O(1), because capacity is infinite

Only doubling steps copy, and those sizes form a geometric series summing to O(n).

8Worst, average, amortized, and space

Three time stories can disagree. Hash-map lookup is O(1) average under a good hash and load factor, but O(n) worst case when every key collides. Dynamic-array append is O(1) amortized and O(n) worst on a single call. Always name which story you mean.

Space has its own bound. Extra arrays are obvious; recursion spends O(\text{max call depth}) on the stack even when the heap looks empty. Binary search's recursive form is O(\log n) stack; an iterative rewrite keeps O(1) auxiliary space for the same time class.

Figure. Name the cost model before comparing algorithms — worst-case, average, amortized, and space answer different questions.

Name the cost model

  1. WorstCeiling over every input of size n — the interview default for Big O.
  2. AverageExpectation under an input (or hash) distribution.
  3. AmortizedTotal over a sequence, divided by operation count.
  4. SpaceHeap extras plus O(\text{depth}) stack for recursion.
Same structure, different stories
Structure / opCommon claimCaveat
Hash map getO(1) averageO(n) worst on collisions
Dynamic array appendO(1) amortizedO(n) on a resize call
Recursive binary searchO(\log n) timeO(\log n) stack space
Iterative binary searchO(\log n) timeO(1) auxiliary space
"Hash map lookup is O(1)" in interview speech usually means
  1. Worst case on every input
  2. Average case under a good hash — worst case can be O(n)
  3. Amortized over a sequence of lookups that resize the table

The casual O(1) is average-case. Pathological collision chains are linear. Amortized is the dynamic-array story, not the hash-get story.

Notes

  • Big O, Omega, Theta: Big O is an asymptotic upper bound, Omega a lower bound, and Theta a tight bound; interviews usually mean worst-case Big O.
  • Dropping Constants: Asymptotic analysis ignores constant factors and lower-order terms, so 3n^2 + 5n + 7 is O(n^2).
  • Common Growth Order: O(1) < O(\log n) < O(n) < O(n \log n) < O(n^2) < O(2^n) < O(n!).
  • Master Theorem: For T(n) = aT(n/b) + f(n), compare f(n) with n^{\log_b a} to determine the asymptotic solution.
  • Amortized Analysis: Averages the cost of an operation over a sequence (e.g., dynamic array append is amortized O(1) despite occasional O(n) resizes).

Formulas

  • Growth ordering: O(1) < O(\log n) < O(n) < O(n \log n) < O(n^2) < O(2^n) < O(n!).
  • Master theorem: T(n)=aT(n/b)+f(n) compares f(n) to n^{\log_b a}.
  • Merge sort recurrence: T(n) = 2T(n/2) + O(n) = O(n \log n).
  • Binary search recurrence: T(n) = T(n/2) + O(1) = O(\log n).
  • Recursion space: O(\text{max recursion depth}) on the call stack.

Exam traps & shortcuts

  • Keep only the fastest-growing term and drop constants: O(n^2 + n) becomes O(n^2).
  • Nested loops over the same n multiply to O(n^2); a loop that halves each step contributes an O(\log n) factor.
  • Use the Master Theorem to solve divide-and-conquer recurrences quickly instead of expanding by hand.
  • Distinguish worst, average, and amortized cost - e.g., hash-map lookup is O(1) average but O(n) worst case.

Reference tables

Standard classes and the recurrences that produce them. Growth order includes 2^n and n!, which dwarf the polynomial bars at fixed n.

Complexity sheet
ItemResultNotes
Growth ladderO(1) < O(\log n) < O(n) < O(n \log n) < O(n^2) < O(2^n) < O(n!)Drop constants / lower terms inside a class
Master TheoremT(n)=aT(n/b)+f(n) vs n^{\log_b a}Case 2 → extra \log n factor
Merge sortT(n)=2T(n/2)+O(n)=O(n \log n)a=b=2, f(n)=\Theta(n)
Binary searchT(n)=T(n/2)+O(1)=O(\log n)Unroll or Master with a=1
Triangular double loopn(n+1)/2 = O(n^2)Still quadratic after dropping 1/2
Doubling appendO(1) amortizedSingle resize still O(n)
Recursion spaceO(\text{max depth})Iterative form may drop the stack

Recap

Asymptotics name an envelope, not a stopwatch. Keep the dominant term, place it on the growth ladder, and say whether the bound is worst, average, or amortized — and whether space includes the call stack.

Bounds
O upper, Ω lower, Θ tight — interviews default to worst-case O.
Simplify
Keep the fastest term; drop coefficients and slower addends.
Ladder
1 < \log n < n < n\log n < n^2 < 2^n < n!.
Loops
Independent nests multiply; a triangle n(n+1)/2 is still O(n^2).
Recurrences
Master: compare f to n^{\log_b a}; or unroll obvious trees.
Amortized
Sequence average (doubling append O(1)) ≠ average-case hash get.
Space
Stack depth counts — recursive binary search spends O(\log n) frames.

Practise Time and Space Complexity

Reading is free and needs no account. Practice, mocks and progress live in the app.

  • 5 exam-style questions on this topic, with explanations
  • A 5-question practice set that ends the chapter
  • Timed mocks scored with the real marking scheme
  • Readiness tracked per topic, kept on your device
Continue with Google — freeNo card, no trial. Works offline once installed.