Big O notation describes an asymptotic upper bound on how a resource requirement grows as the input size, n, increases. The resource is usually execution time or memory. Big O deliberately ignores constant factors and lower-order terms so that two approaches can be compared independently of a particular processor or test run.
If an operation takes 3n + 20 steps, its growth is O(n). If it takes n2 + 5n steps, its growth is O(n2). This does not predict an exact duration; measurement is still required to understand real performance.
| Complexity | Name | Representative operation |
|---|---|---|
| O(1) | Constant | Reading an array element by index |
| O(log n) | Logarithmic | Binary search in a sorted array |
| O(n) | Linear | Scanning every element once |
| O(n log n) | Linearithmic | Merge sort or heap sort |
| O(n2) | Quadratic | Comparing every pair of elements |
| O(2n) | Exponential | Exploring every subset in a direct solution |
| O(n!) | Factorial | Enumerating every permutation |
Best, average, and worst case describe which inputs are being considered. Big O describes a bound. They are related but not interchangeable. For example, a linear search has a best case of O(1) when the first element matches and a worst case of O(n) when the item is last or absent.
Average-case claims require an assumption about the distribution of inputs. Without that model, “average” is not a meaningful guarantee. A worst-case upper bound is often useful because it states what the operation will not exceed asymptotically.
static boolean contains(int[] values, int target) {
for (int value : values) {
if (value == target) {
return true;
}
}
return false;
}
static int countEqualPairs(int[] values) {
int pairs = 0;
for (int i = 0; i < values.length; i++) {
for (int j = i + 1; j < values.length; j++) {
if (values[i] == values[j]) {
pairs++;
}
}
}
return pairs;
}
One operation may occasionally be expensive while a sequence remains efficient. Appending to a dynamic array is normally constant time, but a resize copies the existing elements. Across many appends, the total work is linear, giving an amortized O(1) cost per append. Amortized analysis is a guarantee over a sequence of operations; it is not the same as average-case analysis over random inputs.
A design pattern is judged primarily by responsibilities, coupling, and changeability, but it can also alter runtime behavior. Observer notification is typically proportional to the number of registered observers. A naïve Composite traversal visits every node. Flyweight may reduce memory while adding lookup work. Decorator adds a delegation layer for each wrapper.
Big O alone cannot decide whether a pattern is appropriate. Use it to identify scaling risks, then measure representative workloads and weigh the result against clarity, flexibility, and correctness.
Data Structures and Algorithms