Algorithmic Complexity and Dynamic Programming
A vocabulary for judging how any algorithm scales, and three techniques for making slow ones fast
- read and write Big-O bounds and justify why constants and lower-order terms drop,
- rank the common complexity classes and reason about time/space trade-offs,
- recognise the divide-and-conquer pattern behind algorithms such as merge sort,
- convert an exponential recursion into a linear one with memoization, then into a bottom-up dynamic program.
1. Why we count operations, not seconds
Timing an algorithm with a clock measures your CPU, your compiler flags and today's cache state as much as the algorithm itself. To reason about the algorithm alone we count the number of basic operations it performs as a function of the input size n, and we ask how that count behaves when n becomes large.
Suppose a routine performs 3n + 5 comparisons. The +5 is a fixed start-up cost and the 3 is a per-element constant that depends on the machine. For large n the term that decides the outcome is n. We therefore say the routine is O(n): its running time grows linearly, and doubling the input roughly doubles the work.