AtomLearn
DashboardGoalsGraphAchievementsReviewSign In
Algorithms & Data StructuresNot Started

Big-O Notation

Measure algorithm efficiency — time and space complexity

0%

Knowledge Debt detected

You can study this freely — but your score may plateau if these foundations have gaps. The Mastery badge requires them to be solid.

Explanation

Big-O notation describes how an algorithm's runtime (or memory use) grows as the input size grows. It's the language engineers use to reason about performance at scale.

Big-O growth rates — n = input size, y = operationsnopsO(1) constantO(log n) logarithmicO(n) linearO(n log n) sortO(n²) quadraticO(n²) explodes fast — at n=1000 that's 1,000,000 operations vs O(n log n) at ~10,000

The most common complexities (best to worst):

| Notation | Name | Example | 1000 items | |---|---|---|---| | O(1) | Constant | Dict lookup | 1 op | | O(log n) | Logarithmic | Binary search | ~10 ops | | O(n) | Linear | Loop through list | 1,000 ops | | O(n log n) | Linearithmic | Merge sort | ~10,000 ops | | O(n²) | Quadratic | Nested loops | 1,000,000 ops | | O(2ⁿ) | Exponential | Naive recursion | astronomical |

Reading code for complexity:

```python # O(1) — doesn't grow with input def get_first(lst): return lst[0]

# O(n) — one loop def find_max(lst): m = lst[0] for x in lst: # n iterations if x > m: m = x return m

# O(n²) — nested loops def has_duplicates(lst): for i in range(len(lst)): for j in range(i + 1, len(lst)): # n * n/2 ≈ n² if lst[i] == lst[j]: return True return False

# O(n) — same problem with a set def has_duplicates_fast(lst): return len(lst) != len(set(lst)) # O(n) time, O(n) space ```

Dropping constants and lower terms:

O(2n) → O(n), O(n² + n) → O(n²). Big-O describes the growth *shape*, not the exact count.

Space complexity — how much extra memory: ```python # O(1) space — no extra memory def sum_list(lst): total = 0 for x in lst: total += x return total

# O(n) space — new list same size def doubled(lst): return [x * 2 for x in lst] ```

Examples

Identifying complexity in real code

The same question answered in O(n) vs O(1) — just by choosing a set over a list

# What's the complexity of each?

# 1. Dictionary lookup — O(1)
cache = {"a": 1, "b": 2}
value = cache.get("a")       # O(1) regardless of dict size

# 2. List contains — O(n)
items = [1, 2, 3, ..., 1000]
exists = 999 in items        # scans up to every element

# 3. Set contains — O(1) average
items_set = set(items)
exists = 999 in items_set    # hash lookup, O(1)

# 4. Sorting — O(n log n)
sorted_items = sorted(items)

# Key insight: choosing the right data structure
# changes complexity without changing the algorithm logic

Why O(n²) breaks at scale

O(n²) vs O(n) — 5000x difference at n=10,000

import time

def find_pairs_slow(lst):    # O(n²)
    pairs = []
    for i in range(len(lst)):
        for j in range(i + 1, len(lst)):
            if lst[i] + lst[j] == 0:
                pairs.append((lst[i], lst[j]))
    return pairs

def find_pairs_fast(lst):    # O(n)
    seen = set()
    pairs = []
    for x in lst:
        if -x in seen:
            pairs.append((-x, x))
        seen.add(x)
    return pairs

# At n=10_000:
# slow: ~50 million comparisons
# fast: ~10_000 operations

Next in Algorithms & Data Structures

Arrays & Hash Maps

Continue