We’re Officially Live β€” Lifetime Founding Membership Available for a Limited Time
Free preview

Time & Space Complexity

Big-O describes how cost grows with input size, and on an MCU you also care about the constants it hides, worst-case determinism (WCET), and the space-time tradeoff.

22 min read

In this lesson:


Why two correct programs aren't equal

Searching a sorted 10,000-entry calibration table two ways:

linear scan:   up to 10,000 comparisons
binary search: up to 14 comparisons   (log2(10000) β‰ˆ 13.3)

Both find the value. One scales; one doesn't. Big-O notation captures that difference: how does cost grow as the input size n grows?


What Big-O measures

Big-O describes the growth rate of an algorithm's cost as a function of input size n, keeping only the dominant term and dropping constant factors. It's an upper bound, usually the worst case.

O(nΒ²+5n) and O(nΒ²) are the same class: for large n the nΒ² term dominates.

The classes you must know

Big-O Name n=1000 (rough ops) Example
O(1) constant 1 array index a[i], hash lookup (avg), push/pop
O(log n) logarithmic ~10 binary search, balanced-tree lookup
O(n) linear 1,000 linear scan, sum an array
O(n log n) linearithmic ~10,000 good sorts (merge, heap, quick avg)
O(nΒ²) quadratic 1,000,000 nested loops, insertion/bubble sort
O(2ⁿ) exponential astronomical naive recursive subsets, brute force
ops
 β”‚            O(2^n)   O(n^2)
 β”‚              β•±        β•±
 β”‚             β•±      β•±        O(n log n)
 β”‚            β•±    β•±        β•±
 β”‚          β•±  β•±      β•±        ____ O(n)
 β”‚        β•±β•±     β•±___β•±______________ O(log n)
 β”‚      β•±β•±__β•±___╱──────────────────── O(1)
 └──────────────────────────────────── n

A practical sniff test: a single loop over the input is O(n); a loop inside a loop over the same input is O(nΒ²); halving the search space each step is O(log n).

// O(n) β€” one pass
for (size_t i = 0; i < n; i++) sum += a[i];
 
// O(n^2) β€” every pair
for (size_t i = 0; i < n; i++)
    for (size_t j = 0; j < n; j++)
        if (a[i] == a[j] && i != j) dup = true;

Space complexity

The same idea applied to memory: how does extra memory grow with n? In-place algorithms are O(1) extra space; one that allocates a copy is O(n).

Don't forget the call stack: recursion costs O(depth) stack space. Recursing n deep is O(n) space even if you allocate nothing, and on an MCU with a few KB of stack, that's a real overflow risk (recall the stack lesson).

// O(1) extra space β€” reverses in place
void reverse(int *a, size_t n) { /* two-pointer swap */ }
 
// O(n) space β€” recursion n deep uses n stack frames
int sum_rec(const int *a, size_t n) {
    return n == 0 ? 0 : a[0] + sum_rec(a + 1, n - 1);   // n frames!
}

The embedded caveats Big-O hides

Big-O is essential but it drops exactly the things firmware sometimes cares about most:

Constants matter at small n. Big-O is about large n. On an MCU, n is often tiny (8 sensors, 16 queue slots). An O(nΒ²) insertion sort can beat an O(n log n) quicksort for n=10 because the constant factors and code size are smaller. Measure; don't assume the asymptotically-better algorithm wins at your n.

Worst case is the one that matters (WCET). Hard-real-time code is bounded by its worst-case execution time, not its average. A hash table is O(1) average but O(n) worst (all keys collide), that worst case may be unacceptable in an ISR, where a deterministic O(log n) or a bounded structure is safer.

Memory is a hard ceiling. An O(1)-time hash lookup that needs a 64 KB table doesn't fit a part with 8 KB of RAM. The space term isn't a footnote on embedded, it can be the deciding constraint.


The space-time tradeoff

You can often spend memory to save time, or vice versa. This is the engine behind two later lessons:

The art on embedded is choosing where you sit on that curve given your flash, RAM, and timing budget.


Gotchas


TL;DR

This is just the start

Sign up free to track progress on this lesson, get coding problems with the Run button, and unlock the full interview Q&A.

More in Data Structures & Algorithms

Arrays & Memory LayoutRing Buffers
Linked Lists
Stacks & Queues
Hash Tables
Static vs Dynamic Allocation
Time & Space Complexity | EmbeddedPrep.io