How to Analyze Algorithms
Table of Contents + โ
Hereโs a problem you hit the moment you write any loop. You look at your code and someone asks, โHow fast is this?โ And you have no idea what to say. You feel it runs fine on your laptop, but you canโt prove it will stay fast when the input gets big.
That is exactly what analyzing an algorithm fixes. Analyzing an algorithm means looking at the code and working out how the running time grows as the input grows. We measure that growth with Big O. The good news is there is a simple method. You can use it every time. So let me walk you through it step by step. Then weโll analyze real code in five languages.
๐ฏ What We Are Really Counting
Before any rules, letโs be clear about what we measure. We do not measure seconds on a clock. Different computers run at different speeds, so seconds tell us nothing useful.
Instead we count basic operations. A basic operation is one simple step the computer does, like one comparison, one addition, or one array access. We ask one question: as the input size n grows, how does the number of basic operations grow? That growth is the whole story.
n is the size of the input
Throughout this tutorial, n means the size of the input. If you pass an array of 100 items, then n is 100. Big O describes how the work grows as n gets larger and larger.
๐ ๏ธ The Method to Find Big O
Here is the recipe you will use every single time. The key idea is simple. You count the basic operations, then nested loops multiply, then you drop the small stuff. Follow these steps in order.
- Count the basic operations. Look at the code and count how many simple steps run for an input of size
n. - A single loop over the input is O(n). If a loop runs once for each of the
nitems, that is linear time. - Nested loops multiply. A loop inside a loop, each running
ntimes, givesn ร n, which is O(n^2). - Drop the constants. A step that always runs a fixed number of times, no matter how big
nis, counts as O(1). And2nis just O(n); we throw away the 2. - Keep the biggest term. If you get something like
n^2 + n, only the largest part matters whennis huge. So keepn^2and drop the rest.
That is the entire method. Now letโs see why each step makes sense and run it on real code.
1๏ธโฃ A Single Loop Is O(n)
Letโs start with the simplest case. We have an array, and we want to add up all its numbers. We touch each item exactly once.
So why is this O(n)? Because the loop body runs once for each item:
- If the array has 10 items, the loop runs 10 times.
- If it has 1,000 items, it runs 1,000 times.
- The work grows in a straight line with
n. That is linear time, written O(n).
The steps outside the loop, like creating the total variable and returning it, run just once. They do not depend on n, so they are O(1) and we ignore them next to the loop. Now letโs code it.
Sum an array with a single loop in Python
# Adds up every element. The loop runs once per item, so this is O(n).def sum_array(arr): total = 0 # runs once -> O(1) for x in arr: # runs n times -> O(n) total += x # one basic operation each pass return total # runs once -> O(1)
arr = [10, 20, 30, 40, 50]print("Sum =", sum_array(arr))The output of the above code will be:
Sum = 150Complexity of a single loop
Time Complexity: O(n) โ the loop body runs once per item, so the work grows in a straight line with the input size.
Space Complexity: O(1) โ we only keep one total variable, no matter how big the array gets.
2๏ธโฃ Nested Loops Multiply Into O(n^2)
Now letโs put a loop inside a loop. Say we want to print every pair of items in the array. For each item we walk the whole array again.
So why O(n^2) here? Because the inner loop runs fully for each turn of the outer loop:
- The outer loop runs
ntimes. - For each of those, the inner loop also runs
ntimes. - So the body runs
n ร n, which isn^2times.
When loops are nested like this, you multiply their counts. If the array has 5 items, the inner body runs 25 times. Bump it to 1,000 items and it jumps to a million. That fast growth is what O(n^2) warns you about. Letโs code it.
Print all pairs with nested loops in Python
# A loop inside a loop. Each runs n times, so the body runs n*n -> O(n^2).def print_pairs(arr): for i in arr: # outer loop runs n times for j in arr: # inner loop runs n times each print(f"({i},{j})", end=" ") print()
arr = [1, 2, 3]print_pairs(arr)The output of the above code will be:
(1,1) (1,2) (1,3) (2,1) (2,2) (2,3) (3,1) (3,2) (3,3)Two loops side by side do NOT multiply
Multiplying only happens when one loop sits inside another. Two separate loops one after the other are added, not multiplied. So n + n is 2n, which is still O(n). Only nesting gives you O(n^2).
Complexity of nested loops
Time Complexity: O(n^2) โ the inner loop runs n times for each of the n outer turns, so the work grows with the square of the input.
Space Complexity: O(1) โ we are not storing anything that grows with n.
3๏ธโฃ Drop Constants and Keep the Biggest Term
This is the step that confuses people, so letโs slow down. Imagine a function that does a few separate things on the same array. It runs a couple of loops, then one nested loop.
Counting the raw operations, we might get something like 2n + n^2. That looks messy. Big O cleans it up with two rules.
First, drop the constants. We do not care about the 2 in 2n. Constants do not change how the work grows. Even doubling the computer speed only changes the constant. We are measuring growth, not exact counts. So 2n becomes n.
Then keep the biggest term. We had n + n^2. When n is small the two look close, but watch what happens as n grows:
- At
n = 10, you get10 + 100 = 110. Then^2part is already most of it. - At
n = 1000, you get1000 + 1000000. Thenpart is now a tiny crumb. - So as
nheads toward infinity, then^2term swallows everything else.
That is why we keep only the largest term and drop the rest. The answer is O(n^2). Letโs see it in code.
Mixed work, then reduce to one term in Python
# Two single loops (n + n = 2n) plus one nested loop (n^2).# Total 2n + n^2 reduces to O(n^2) after dropping constants and smaller terms.def analyze(arr): n = len(arr) single = 0 pairs = 0
for x in arr: # O(n) single += x for x in arr: # O(n) single += x * 2
for i in range(n): # O(n^2) for j in range(n): pairs += 1
print("single work counted twice =", single) print("nested pairs counted =", pairs)
analyze([1, 2, 3, 4])The output of the above code will be:
single work counted twice = 30nested pairs counted = 16The shortcut you'll use forever
Once you trust these rules, you stop counting every line. You just scan for the worst part of the code. One nested loop over the input? Itโs O(n^2). One plain loop? O(n). No loop that depends on n? O(1). The biggest piece wins.
๐ Quick Reference for Common Shapes
Most code you analyze falls into one of a few shapes. Here is a cheat sheet you can match your code against.
| Code shape | Big O | Why |
|---|---|---|
| No loop over the input | O(1) | A fixed number of steps, no matter how big n is |
| One loop over n items | O(n) | Work grows in a straight line with n |
| Two separate loops | O(n) | n + n = 2n, and we drop the constant |
| A loop inside a loop | O(n^2) | Nested loops multiply: n times n |
| Halving the input each step | O(log n) | The input shrinks fast, like in binary search |
โ ๏ธ Common Mistakes and Misconceptions
A few traps catch beginners over and over. Letโs clear them out.
- Keeping the constants. Writing O(2n) or O(n + 5) misses the point. Big O ignores constants, so it is just O(n). The notation is about growth, not exact counts.
- Adding nested loops instead of multiplying. A loop inside a loop is
n ร n, notn + n. The multiply is what makes it O(n^2). - Multiplying loops that are not nested. Two loops one after the other are added. They stay O(n). Only nesting multiplies.
- Keeping every term. Once you have
n^2 + n + 5, onlyn^2survives. The smaller pieces vanish asngrows, so drop them. - Counting a fixed-size loop as O(n). A loop that always runs 100 times, no matter the input, is O(1). It only counts as O(n) if it grows with
n.
๐งฉ What Youโve Learned
You can now look at a piece of code and name its Big O with confidence. Hereโs what youโve picked up.
- โ
We measure growth in basic operations as
ngrows, never in seconds on a clock. - โ A single loop over the input is O(n), and a fixed number of steps is O(1).
- โ
Nested loops multiply, so a loop inside a loop over
nitems is O(n^2). - โ
Drop the constants, because
2nis just O(n). - โ
Keep only the biggest term, so
n^2 + nreduces to O(n^2). - โ The shortcut is to scan for the worst part of the code, since the largest term wins.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What is the time complexity of a single loop that runs once for each of the n items?
Why: A single loop over n items runs n times, so the work grows in a straight line with the input, which is O(n).
- 2
You count 2n + n^2 operations. What is the Big O?
Why: Drop the constant on 2n and keep only the largest term, n^2, so the answer is O(n^2).
- 3
A loop over n items sits inside another loop over n items. What is the complexity?
Why: Nested loops multiply, so n times n gives n^2, which is O(n^2).
- 4
Two separate loops, each over n items, run one after the other. What is the Big O?
Why: Separate loops are added, giving n + n = 2n, and after dropping the constant it is O(n).
๐ Whatโs Next?
Youโve got the method. Now connect it to the ideas it builds on and the resources it touches.
- Big O Notation explains the notation itself and the full family of growth rates.
- Time Complexity goes deeper on how running time grows for different algorithms.
- Space Complexity does the same analysis for the memory your code uses.
Try this method on the next loop you write, and โhow fast is this?โ will turn into a quick, confident answer.