Introduction
Asymptotic notation says how to compare growth rates. This note shows how to obtain the function to compare: how to count the work done by loops, nested loops and recursive calls, and how much memory a program needs beyond its input. The counts below reduce to one of the loop patterns, an exact iteration count, or a stack-depth argument.
Prerequisites
- Asymptotic Notation: , , , and the growth hierarchy.
- Basic summations: arithmetic and geometric series.
Core concepts
The cost model
Analysis assumes the RAM model (CLRS 3e, Section 2.2): instructions run one after another, and each basic operation (arithmetic on word-sized numbers, comparison, assignment, array indexing, a function call and return) takes constant time. Running time is then proportional to the number of basic operations executed.
Two choices must be stated for every analysis:
- Input size . For sorting and searching it is the number of elements; for a graph it is the pair ; for arithmetic on a number it is the number of bits, .
- Case. Worst case (maximum over inputs of size ), best case (minimum), or average case (expectation under a stated distribution of inputs).
The input-size choice matters. Trial division tests whether is prime with about divisions. That is polynomial in the value but in the number of bits , so it is exponential in the input size. Algorithms polynomial in the numeric value but not in its bit length are called pseudo-polynomial; the 0/1 knapsack algorithm is the standard example.
Rules for combining costs
- Sequence. the cost of is the sum of the costs. Asymptotically, the larger term dominates.
- Conditional. worst case of
if C then S1 else S2is cost(C) plus the larger branch. - Loop. the sum, over all iterations, of the cost of the body plus the test. If the body cost does not depend on the iteration, this is (number of iterations) (body cost).
- Nested loops. work from the inside out, expressing the inner count as a function of the outer variable, then sum.
- Function call. the cost of the body for those arguments; for recursion this gives a recurrence.
Summations used most often
| Sum | Closed form or bound | Type |
|---|---|---|
| exact, | ||
| exact, | ||
| for constant | asymptotic | |
| exact | ||
| , | less than | bound, |
| asymptotic, | ||
| asymptotic | ||
| exact |
A geometric series is dominated by its largest term: an increasing one is and a decreasing one is .
Loop patterns
Pseudocode is CLRS-like: <- is assignment, loops are inclusive of both bounds, and arrays are 1-indexed.
Pattern 1: dependent nested loop (triangle)
count <- 0
for i <- 1 to n
for j <- 1 to i
count <- count + 1The inner loop runs times, so . Halving the work (a triangle instead of a square) does not change the order. With three nested loops the count is .
Pattern 2: multiplicative loop
i <- 1
while i < n
i <- 2 * iAfter iterations , and the loop stops when . So it runs exactly times for (for example 7 times for , 10 for , 11 for ). Halving loops (i <- floor(i/2) until ) behave the same way: iterations from . The base of the multiplication only changes the constant: multiplying by 3 gives .
Pattern 3: logarithmic inner loop
for i <- 1 to n
j <- 1
while j <= i
j <- 2 * jThe inner loop runs times, so the total is . For the counts are 1, 2, 2, 3, 3, 3, 3, 4, total 21.
Pattern 4: harmonic loop
for i <- 1 to n
j <- 1
while j <= n
j <- j + iFor a fixed the inner loop runs times. The total is , which lies between and , so it is . For : . This pattern appears in the sieve of Eratosthenes and in "for each divisor" loops.
Pattern 5: geometric outer loop with linear inner loop
i <- 1
while i <= n
for j <- 1 to i
(constant work)
i <- 2 * iThe inner loop runs times, where . The total is less than , so the fragment is , not . For the total is 31. Multiplying the number of outer iterations () by the largest inner count () gives only an upper bound.
Pattern 6: repeated square root
i <- n
while i > 2
i <- floor(sqrt(i))If , the values are , so the loop runs times: 4 times for . In general the count is .

Pattern 7: loops whose total is amortised
j <- 1
for i <- 1 to n
while j <= n and A[j] < A[i]
j <- j + 1The inner while can run up to times for one , but never decreases and never exceeds , so the inner body runs at most times in total. The fragment is in the worst case, not . Always ask whether an inner index is reset; this is the key to merge, two-pointer scans and BFS-style analyses.
Pattern 8: square-root bounds
i <- 1 s <- 0; i <- 0
while i * i <= n while s < n
i <- i + 1 i <- i + 1
s <- s + iThe left loop runs times (10 for , 9 for ). In the right loop, after iterations , so it stops at the least with : , 14 for (since ). A loop variable that grows by an increasing step is the usual disguise of .
Pattern 9: nested doubling loops
i <- 1
while i < n
j <- 1
while j < i
j <- 2 * j
i <- 2 * iThe outer loop takes for with , and the inner loop runs times for . The total is : 45 for (), 55 for (). Neither nor is right.
Pattern 10: three dependent loops
for i <- 1 to n
for j <- 1 to i
for k <- 1 to j
count <- count + 1The count is the number of triples , which is : 20 for , 220 for . With strict inequalities () it is instead. Both are , but an exact-count question distinguishes them.
Best, worst and average case of a fragment
Linear search for a key in :
LINEAR-SEARCH(A, n, x)
for i <- 1 to n
if A[i] = x then return i
return NIL| Case | Comparisons with | Order |
|---|---|---|
| Best: | 1 | |
| Worst: absent or at | ||
| Average: present, each position equally likely |
The average case needs an explicit assumption; here, that is present and equally likely at each position. Change the assumption (for example, absent with probability ) and the exact average changes, though here it stays .
Space complexity
What is counted
- Input space: the memory holding the input. Usually not charged to the algorithm.
- Auxiliary space: extra memory used during execution: local variables, temporary arrays, and the recursion stack.
- Total space = input space + auxiliary space.
"Space complexity" may mean auxiliary space or total space; check the wording. An algorithm is in place if it uses auxiliary space apart from the input. Some books also call quicksort in place although its stack uses to words; when it matters, state which convention is meant.
The recursion stack
Each active call keeps a stack frame (parameters, locals, return address) of constant size in typical analyses. Auxiliary space for recursion is therefore times the frame size, not the total number of calls.
Here is the golden ratio.
| Algorithm | Total calls | Maximum depth | Auxiliary space |
|---|---|---|---|
| Recursive factorial of | |||
| Recursive binary search on elements | ; iterative version | ||
| Naive Fibonacci | |||
| Merge sort | levels | for the merge buffer | |
| Quicksort | best, worst | as the depth; if it recurses on the smaller part and loops on the larger |
The Fibonacci row shows the distinction clearly: exponential time, linear space, because only one root-to-leaf path of calls is on the stack at any moment. (Call counts are taken with base cases and ; see Recursion Tracing and Call Counting.)
Time-space trade-offs
Memory can buy time. Storing the Fibonacci values computed so far (dynamic programming) cuts time from exponential to at the cost of space, or space if only the last two values are kept. A hash table trades space for expected lookups.
Important properties and observations
- Count iterations exactly when the required result is a counter value; use asymptotics only when an order is required. Watch
<versus<=in loop tests: they change exact counts by one and, for multiplicative loops, change into . - Multiplying the outer count by the maximum inner count gives an upper bound only (Patterns 5 and 7).
- A loop that halves or doubles is logarithmic regardless of the factor, as long as the factor is a constant greater than 1. A loop that adds a constant is linear. A loop that squares (or square-roots) is doubly logarithmic.
- A recursion's space is its depth, not its number of calls.
- Time is at least space for algorithms that write the memory they use (each cell written costs a step), so an algorithm that allocates and initialises memory takes time.
GATE examination perspective
- Exact counts. The quantity to determine is the final value of a counter or the number of times a statement runs. Tabulate small and look for the closed form; then check one value.
- Order of a fragment. identify which pattern each loop follows; check whether inner bounds depend on outer variables and whether inner indices reset.
- Space questions. separate input, auxiliary and stack space; for recursion, find the deepest chain of calls.
- Traps. Pattern 5 is ; Pattern 3 is ; the harmonic loop is ; nested doubling loops are ; an increasing step is ; a "nested" loop with a non-resetting index is linear.
Summary
Sources
- T. H. Cormen, C. E. Leiserson, R. L. Rivest, C. Stein, Introduction to Algorithms, 3rd ed., MIT Press, 2009: Section 2.2 (analysing algorithms, the RAM model, worst and average case).