Big O notation
On This Lesson 11 topics
Lesson Overview
Last lesson you described growth in sentences: "the steps double when n doubles", "one more step per doubling". Sentences are slow to write and easy to misread. Computer scientists use a short, standard notation instead, called Big O. This lesson teaches you to read it and write it for the five classes you will meet almost everywhere: O(1), O(log n), O(n), O(n log n), and O(n²).
Why this matters: Big O is the shared vocabulary of code reviews, design discussions, and interviews. When someone says "that lookup is O(n)", they are telling you how it will behave when the data grows.
This lesson builds on "Why we measure growth, not seconds" (BEG-039-L01), on counting steps (BEG-017), and on the loops and arrays you already know.
Learning Objectives
By the end of this lesson you will be able to:
- Read and write O(1), O(log n), O(n), O(n log n), and O(n²).
- Simplify an expression such as
3n + 5orn² + nto its Big O class. - Explain the two simplification rules: drop constant factors, drop smaller terms.
- Order the five classes from slowest-growing to fastest-growing.
- Match a small piece of code to its class.
- Say what Big O does not tell you.
Written Explanation
What Big O says
Big O describes how the number of steps grows with the input size n, ignoring details that do not change the shape of the growth. We write O(...) with the growth shape inside the brackets, and read it aloud as "order of" or "Big O of".
Take a method that does 3 steps for every element, plus 5 steps of setup. Its step count is 3n + 5. I ran such a method for three sizes on JDK 21:
n | 10 | 100 | 1,000 |
|---|---|---|---|
Steps (3n + 5) | 35 | 305 | 3,005 |
Multiply n by 10 and the steps multiply by about 10. The setup is a rounding error and the factor 3 does not change the shape. This method is O(n), said "order n" or "linear".
The two simplification rules
- Drop constant factors.
3nand100nandnhave the same shape: all grow in step withn. Write O(n). - Keep only the fastest-growing term. In
n² + n, oncenis large,n²dominates. Forn = 1,000,n²is 1,000,000 andnis only 1,000, a tenth of a percent of the total. Write O(n²).
A fixed amount of work, like 100 steps whatever n is, is O(1): we write 1 for "a constant".
Big O is about large n. It deliberately ignores the cost of one step, which is why it never replaces a timing; it tells you the trend.
The five classes
| Class | Name | Meaning | Typical code |
|---|---|---|---|
| O(1) | constant | the same work for any n | a[i], list.get(i) on an ArrayList |
| O(log n) | logarithmic | the problem halves each step | binary search |
| O(n) | linear | touch each item once | one loop over the data |
| O(n log n) | linearithmic | a halving process applied n times | efficient sorting |
| O(n²) | quadratic | every item against every item | two nested loops over the data |
log n here means the number of times you can halve n until you reach 1: about 3 for 10, about 10 for 1,000, about 20 for 1,000,000. We use base 2 in the examples, but the base does not matter in Big O, because changing the base only multiplies by a constant, and rule 1 drops constants.
Here is what the classes cost for three sizes. The values are exact arithmetic, rounded:
n | O(1) | O(log n) | O(n) | O(n log n) | O(n²) |
|---|---|---|---|---|---|
| 10 | 1 | 3 | 10 | 33 | 100 |
| 1,000 | 1 | 10 | 1,000 | about 10,000 | 1,000,000 |
| 1,000,000 | 1 | 20 | 1,000,000 | about 20 million | 1,000,000,000,000 |
The order from slowest-growing (best) to fastest-growing (worst) is:
O(1) < O(log n) < O(n) < O(n log n) < O(n²)
Look at the last row. For a million items the quadratic column is a trillion steps, while n log n is twenty million. That gap is why we care about the class.
Reading code
Four small methods, each with a step counter added. I ran them for n = 10, 100, and 1,000.
static int first(int[] a) { steps++; return a[0]; } // O(1)
static int sum(int[] a) { // O(n)
int s = 0;
for (int x : a) { steps++; s += x; }
return s;
}
static int halvings(int n) { // O(log n)
int c = 0;
for (int i = n; i > 1; i /= 2) { steps++; c++; }
return c;
}
static int pairs(int[] a) { // O(n²)
int c = 0;
for (int i = 0; i < a.length; i++)
for (int j = 0; j < a.length; j++) { steps++; if (a[i] == a[j]) c++; }
return c;
}n=10 first=1 sum=10 halvings=3 pairs=100
n=100 first=1 sum=100 halvings=6 pairs=10000
n=1000 first=1 sum=1000 halvings=9 pairs=1000000Each column matches its class: constant, in step with n, one more step per doubling (roughly), and n × n.
What Big O does not say
- It does not give seconds. An O(n) method with a slow step can lose to an O(n²) method with a very fast step on tiny input.
- It does not say which of two methods in the same class is faster. Two O(n) loops can differ by a factor of ten.
- In everyday work Big O is quoted as the tightest simple class you can justify for the case you are discussing. Strictly, it is an upper bound on growth, a detail university texts treat formally. I did not fetch such a text for this lesson, so treat that sentence as background rather than a cited claim. L05 returns to cases.
Examples
Example 1: simplify the expression (simple)
Rewrite each as Big O, applying the two rules.
| Steps | Rule used | Big O |
|---|---|---|
7 | constant | O(1) |
5n + 2 | drop 2 and the factor 5 | O(n) |
n² + 4n + 9 | keep the fastest-growing term, drop the factor | O(n²) |
2n + n log n | n log n grows faster than n | O(n log n) |
Example 2: classify the method (practical)
static int lookupPrice(int[] prices, int productId) {
return prices[productId]; // one array read
}
static int totalPrice(int[] prices) {
int total = 0;
for (int p : prices) total += p; // one pass
return total;
}
static boolean hasSameValuePair(int[] prices) {
for (int i = 0; i < prices.length; i++)
for (int j = i + 1; j < prices.length; j++)
if (prices[i] == prices[j]) return true;
return false;
}lookupPrice is O(1): one read, whatever the array length. totalPrice is O(n): one loop over the data. hasSameValuePair has a loop inside a loop over the same array, so in the worst case (no equal pair) it is O(n²). L03 explains how to count such loops carefully.
Example 3: choose between two algorithms (realistic)
A report has to look up one product among 1,000,000 sorted product IDs.
- Read every ID until it matches: O(n), up to 1,000,000 steps.
- Binary search on the sorted array: O(log n), about 20 steps (the BEG-017 experiment measured 19 for a missing target).
For one lookup the difference is imperceptible. If the report does 10,000 lookups, it is 10,000,000,000 steps against about 200,000. Big O made that visible without running the code.
For sorting you would not write your own method. The Java documentation for Arrays.sort(int[]) describes a Dual-Pivot Quicksort and states O(n log n) performance, as BEG-017 noted. Choosing a library sort over a hand-written nested-loop sort is a choice between O(n log n) and O(n²).
Backend Insight
Big O shows up whenever a backend system has to handle more data than you tested with:
- A search that is O(n) per request is fine for 1,000 users and a problem for 10 million; the same search through an index is O(log n) and stays fast.
- O(1) operations are the ones to build hot paths from, such as reading an array element or, as you will see in L06, looking up a key in a
HashMap(average-case). - An O(n²) job over a list of orders can work in development and take hours in production. Spotting nested loops over the same data in a code review is one of the cheapest ways to prevent that.
Big O is also a conversation tool. "This is O(n²), can we make it O(n log n)?" is a precise question that colleagues understand at once.
Common Mistakes
Mistake 1: Keeping the constants
- Incorrect: "This loop is O(3n)."
- Correct: "This loop is O(n)."
Mistake 2: Keeping the smaller terms
- Incorrect: "
n² + nis O(n² + n)." - Correct: "
n² + nis O(n²)."
Mistake 3: Treating O(1) as "fast"
O(1) means the work does not depend on n. A constant of a million steps is still O(1) and still slow. It means "does not grow", not "cheap".
Mistake 4: Confusing the class with the exact count
A method with 1,000 steps for n = 1,000 and one with 50 steps for n = 1,000 can both be O(n). Big O hides constants on purpose.
Exercises
Try each exercise yourself before opening the solutions.
Level A: Foundation
A1. Simplify to Big O: 12, 8n, n + 50, 4n² + 3n.
Show solution A1
A1. O(1); O(n); O(n); O(n²).
A2. List the five classes of this lesson from best to worst growth.
Show solution A2
A2. O(1), O(log n), O(n), O(n log n), O(n²).
A3. What is the Big O of reading names[3] from a String[], and of printing every element of it once?
Show solution A3
A3. names[3] is O(1). Printing all elements once is O(n).
Level B: Application
B1. Classify each: (a) a loop for (int i = 1; i < n; i *= 2), (b) two loops one after the other, each over n, (c) Arrays.sort on an int[] (per its documentation), (d) a loop over n containing a loop over n.
Show solution B1
B1. (a) O(log n). (b) O(n): n + n = 2n, and the factor 2 is dropped. (c) O(n log n). (d) O(n²).
B2. A method does n² / 2 steps. A colleague says it must be better than n² and so is not O(n²). Explain what is wrong.
Show solution B2
B2. n² / 2 still contains the factor 1/2, which is a constant and is dropped, so it is O(n²). Halving the steps shrinks the cost by a constant factor but does not change the shape: doubling n still quadruples the work.
B3. Complete the table with the approximate steps for n = 1,000 for O(log n), O(n), and O(n²). Which of the three would you accept for a method called on every web request, and why?
Show solution B3
B3. About 10 steps for O(log n), 1,000 for O(n), and 1,000,000 for O(n²). O(log n) is the safest for every request; O(n) may be acceptable if n stays small; O(n²) at a million steps per request would not survive heavy traffic.
Level C: Challenge
C1. Write static int countEvens(int[] a) that returns how many values are even. Add a steps counter that increases once per element. Print the step count for arrays of 10, 100, and 1,000 elements and give the Big O.
Show solution C1
C1.
static long steps;
static int countEvens(int[] a) {
int count = 0;
for (int x : a) {
steps++;
if (x % 2 == 0) count++;
}
return count;
}The steps equal the array length: 10, 100, and 1,000. The method is O(n).
C2. Method X does 100n steps. Method Y does n² steps. For which n is Y cheaper, equal, and more expensive than X? What does it tell you about O(n) and O(n²)?
Show solution C2
C2. Y is cheaper when n² < 100n, that is when n < 100. They are equal at n = 100. For n > 100, Y costs more, and the gap grows quickly. So an O(n²) method can win on small input, but for large enough n an O(n) method always wins, whatever the constant.
Mini Challenge
Choose three methods from your earlier projects (or write them): one that reads a single element, one with a single loop, one with nested loops.
- For each, write its step count as a formula in
n(for example2n + 1). - Simplify each formula to Big O using the two rules.
- Add a
stepscounter to each and run it forn= 10, 100, and 1,000 to check that your formula matches.
Check your work: the formulas agree with the printed counts, and your Big O has no constants and no smaller terms.
Summary & Next Lesson
Key takeaways
- Big O names the shape of growth of the steps as
ngrows. - Drop constant factors and keep only the fastest-growing term.
- Order: O(1) < O(log n) < O(n) < O(n log n) < O(n²).
- Big O ignores the cost of one step, so it predicts trends, not seconds.
- O(1) means "does not depend on
n", not "cheap".
Next lesson
The next lesson, "Analyzing loops and nested loops", gives a procedure for finding the Big O of real code by looking at its loops, including the hidden loops inside library methods such as list.contains.
Self-check
- What is the Big O of
5n² + 20n + 7? - Which is faster-growing: O(n log n) or O(n²)?
- Why do we drop constant factors?
- What does O(log n) mean in words?
- Does Big O tell you which of two O(n) methods runs faster?
Show answers
- O(n²).
- O(n²).
- A constant factor does not change the shape of the growth, and Big O describes only the shape.
- The problem is cut down (for example halved) at each step, so doubling
nadds only about one step. - No. Both are the same class; only a timing or a closer look at the cost per step can tell them apart.
References
- Princeton University, Analysis of Algorithms (Algorithms, 4th edition). It supports the idea of dropping low-order terms and constants and the common order-of-growth classes (constant, logarithmic, linear, linearithmic, quadratic). The page uses tilde notation and does not define Big O formally, so the statement that Big O is formally an upper bound is background knowledge, not verified here.
- Oracle, Arrays (Java SE 21 API). The documentation for
sort(int[])describes a Dual-Pivot Quicksort with O(n log(n)) performance. - Oracle, ArrayList (Java SE 21 API). The class description says
getandsizerun in constant time. - Oracle, HashMap (Java SE 21 API). It says
getandputtake constant time, assuming the hash function spreads the keys well. - Step counts shown were produced by running the code on JDK 21. The statement that the logarithm base is irrelevant is standard mathematics, not fetched.
Practice
Apply this lesson with the module's lab, exercises and quiz.
Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.