Time complexity measures how an algorithm's runtime grows as input size increases, while space complexity measures how much memory it uses. Both are expressed using Big-O notation to describe worst-case, average-case, or best-case behavior. Analyzing complexity lets us compare algorithms and choose the most efficient solution for a given problem size. Common complexity classes include O(1), O(log n), O(n), O(n log n), O(n²), and O(2ⁿ).
Start by analyzing simple loops and nested loops, counting operations as a function of n before abstracting to Big-O. Compare concrete runtimes against theoretical predictions for small inputs. Practice deriving complexity for recursion using substitution or the Master Theorem.
From Big-O notation you learned to express how a quantity grows relative to input size n. Time and space complexity apply that tool to two concrete resources: computation steps and memory. Time complexity counts how many operations an algorithm performs as a function of n. Space complexity counts how many memory cells it occupies. Both use Big-O to describe growth in the worst case (usually) or average case, and both abstract away constant factors — O(2n) and O(n) are the same complexity class.
The practical importance of complexity becomes clear when n gets large. An O(n²) algorithm on n=1,000 takes a million operations; on n=1,000,000 it takes a trillion. An O(n log n) algorithm on n=1,000,000 takes only about 20 million operations. These differences are not tuning details — they determine whether a program finishes in milliseconds or never. Developing intuition for complexity classes helps you predict this before you write a single line of code.
Nested loops are the most common source of O(n²) complexity: an outer loop runs n times, and an inner loop runs n times per iteration, giving n² total operations. A single loop is O(n). Halving the input on each step (binary search, balanced tree operations) produces O(log n). Algorithms that both iterate and halve — like merge sort — land at O(n log n), which is why that complexity class is so important. Recursion can be analyzed by drawing the recursion tree or applying the Master Theorem, which you will encounter formally in amortized analysis.
Space complexity is often overlooked but equally important in practice. An algorithm's space usage includes both the input itself and auxiliary space — extra memory allocated during execution (call stack frames, temporary arrays, hash tables). Some algorithms are in-place, using O(1) auxiliary space regardless of input size (like insertion sort); others require O(n) extra memory (like merge sort's temporary arrays). When memory is constrained — embedded systems, working with data larger than RAM — a higher time complexity may be acceptable if it saves memory. The time-space trade-off is a recurring theme in algorithm design.
Finally, note that worst-case, average-case, and best-case complexities can all differ. Quicksort is O(n²) in the worst case but O(n log n) on average and in practice with good pivot selection. Reporting only one number can be misleading. When analyzing or comparing algorithms, ask which case the stated complexity describes — and whether that case is common or pathological in your actual usage.