Keyboard shortcuts

Press โ† or โ†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Complexity Analysis: Complete Guide

๐Ÿ“Š Big-O Notation Hierarchy

O(1) < O(log n) < O(โˆšn) < O(n) < O(n log n) < O(nยฒ) < O(2โฟ) < O(n!)

Excellent โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Terrible

O(1)       Constant    Hash lookup, array access
O(log n)   Logarithmic Binary search, balanced BST ops
O(โˆšn)      Sublinear   Prime factorization
O(n)       Linear      Single pass through array
O(n log n) Linearithmic Merge sort, heap sort
O(nยฒ)      Quadratic   Nested loops, bubble sort
O(2โฟ)      Exponential Subsets, recursive Fibonacci
O(n!)      Factorial   Permutations, brute force TSP

๐Ÿ”ข Common Data Structure Operations

Arrays

OperationStatic ArrayDynamic Array
AccessO(1)O(1)
SearchO(n)O(n)
AppendN/AO(1)*
InsertO(n)O(n)
DeleteO(n)O(n)

*Amortized

Linked Lists

OperationSinglyDoubly
Access by indexO(n)O(n)
SearchO(n)O(n)
Insert at headO(1)O(1)
Insert at tailO(n)O(1)
Delete at headO(1)O(1)
Delete by referenceO(n)O(1)

Hash Maps

OperationAverageWorst Case
InsertO(1)O(n)
DeleteO(1)O(n)
SearchO(1)O(n)

Trees

OperationBST (avg)BST (worst)Balanced BST
SearchO(log n)O(n)O(log n)
InsertO(log n)O(n)O(log n)
DeleteO(log n)O(n)O(log n)

Heaps

OperationBinary Heap
InsertO(log n)
Extract min/maxO(log n)
PeekO(1)
Build from arrayO(n)
HeapifyO(log n)

Graphs (V = vertices, E = edges)

OperationAdj. ListAdj. Matrix
Add vertexO(1)O(Vยฒ)
Add edgeO(1)O(1)
Check edgeO(V)O(1)
Find neighborsO(V)O(V)
BFS/DFSO(V + E)O(Vยฒ)

๐Ÿ“ Sorting Algorithms Comparison

AlgorithmBestAverageWorstSpaceStableIn-Place
Bubble SortO(n)O(nยฒ)O(nยฒ)O(1)YesYes
Selection SortO(nยฒ)O(nยฒ)O(nยฒ)O(1)NoYes
Insertion SortO(n)O(nยฒ)O(nยฒ)O(1)YesYes
Merge SortO(n log n)O(n log n)O(n log n)O(n)YesNo
Quick SortO(n log n)O(n log n)O(nยฒ)O(log n)NoYes
Heap SortO(n log n)O(n log n)O(n log n)O(1)NoYes
Counting SortO(n + k)O(n + k)O(n + k)O(k)YesNo
Radix SortO(nk)O(nk)O(nk)O(n + k)YesNo

When to use what:

  • Merge Sort: When stability matters, linked lists, external sort
  • Quick Sort: General purpose, best average case, in-place
  • Heap Sort: Guaranteed O(n log n), in-place, but not stable
  • Insertion Sort: Nearly sorted data, small arrays (< 50 elements)
  • Counting/Radix: Integer data with known range

๐Ÿงฎ Amortized Analysis

What is Amortized Analysis?

Average performance of each operation over a sequence of operations, not worst-case of single operation.

Dynamic Array Example

Append operations: [1] [2] [3] [4] [5] [6] [7] [8]
Capacity changes:   1   2   2   4   4   4   4   8
Copy operations:    0   1   0   2   0   0   0   4

Total copies for 8 appends: 1 + 2 + 4 = 7
Amortized cost per append: 7/8 โ‰ˆ O(1)

Key Insight

Individual operations may be O(n), but over n operations the total is O(n), so amortized is O(1).

๐Ÿ” Analyzing Code Complexity

Nested Loops

# O(nยฒ) - Both loops depend on n
for i in range(n):
    for j in range(n):
        print(i, j)

# O(n * m) - Different sizes
for i in range(n):
    for j in range(m):
        print(i, j)

# O(n) - Inner loop doesn't start from 0
for i in range(n):
    for j in range(i, n):  # Sum = n + (n-1) + ... + 1 = n(n+1)/2
        print(i, j)        # Actually O(nยฒ)

Recursion

# O(2โฟ) - Each call branches into 2
def fib(n):
    if n <= 1: return n
    return fib(n-1) + fib(n-2)

# O(n) - Single branch, linear recursion
def factorial(n):
    if n <= 1: return 1
    return n * factorial(n-1)

# O(log n) - Halving each time
def binary_search(arr, target):
    mid = len(arr) // 2
    if arr[mid] == target: return mid
    elif arr[mid] < target: return binary_search(arr[mid+1:], target)
    else: return binary_search(arr[:mid], target)

# O(n log n) - Divide and conquer (merge sort pattern)
def merge_sort(arr):
    if len(arr) <= 1: return arr
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    return merge(left, right)

Master Theorem

For recurrences of the form: T(n) = aT(n/b) + O(nแตˆ)

If a < bแตˆ:  T(n) = O(nแตˆ)
If a = bแตˆ:  T(n) = O(nแตˆ log n)
If a > bแตˆ:  T(n) = O(n^(log_b a))

Examples:
- Binary Search: T(n) = T(n/2) + O(1) โ†’ a=1, b=2, d=0 โ†’ O(log n)
- Merge Sort: T(n) = 2T(n/2) + O(n) โ†’ a=2, b=2, d=1 โ†’ O(n log n)
- Strassen: T(n) = 7T(n/2) + O(nยฒ) โ†’ a=7, b=2, d=2 โ†’ O(n^2.81)

โš–๏ธ Space Complexity

Common Space Patterns

O(1)       - In-place algorithms, constant variables
O(log n)   - Recursive call stack (binary search)
O(n)       - New array, hash map, recursive stack (linear)
O(nยฒ)      - 2D matrix, adjacency matrix
O(n + m)   - Graph adjacency list

Space-Time Trade-offs

Trade-offExample
Hash map for O(1) lookupTwo Sum (space for speed)
MemoizationFibonacci (cache for recomputation)
Sorting firstTwo Sum II (sort for two-pointer)
Bit manipulationFinding duplicates (space-efficient)

๐Ÿ“‹ Quick Reference: Common Operations

Operation              Time        Space
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Array access           O(1)        -
Array search           O(n)        -
Binary search          O(log n)    O(1) iterative / O(log n) recursive
Hash lookup            O(1) avg    O(n)
BST insert/search      O(log n)    O(1)
Heap insert            O(log n)    O(1)
Heap extract           O(log n)    O(1)
BFS/DFS                O(V+E)     O(V)
Dijkstra               O((V+E)logV) O(V)
Topological sort       O(V+E)     O(V)
Union Find (amortized) O(ฮฑ(n))    O(n)

๐ŸŽฏ Interview Tips for Complexity

  1. Always state complexity after writing your solution
  2. Analyze both time and space
  3. Consider best, average, and worst cases when relevant
  4. Use amortized analysis for dynamic arrays and union-find
  5. Mention the trade-off if youโ€™re using extra space for speed

๐Ÿ”— Cross-References