Beginner: foundations · Thinking in Big-O · topic 4 of 61 · 35 min
Best, average and worst case; analysing loops and recursion
Explanation
One algorithm, three cases
Linear search looks at items one by one until it finds the target. If the target is the first item, it takes one step: that is the best case, O(1). If the target is last or missing, it checks all n items: the worst case, O(n). On average, for a target that is present at a random position, it checks about n/2 items, which is still O(n).
The worst case is the safe promise: the code will never be slower than this. So when someone says "linear search is O(n)" they mean the worst case. When an interviewer asks for the complexity, give the worst case unless they ask for something else, and mention the average case if it is very different.
Quicksort, which you will meet in Module 6, is the famous example where the cases differ. On average it takes O(n log n). But if the pivot it picks is always the smallest or largest item (for example, always picking the first item of an already sorted list), it degrades to O(n²). Library sorts avoid this problem; Python's sorted() is O(n log n) in the worst case.
Loops that halve or double
A logarithm answers the question "how many times can I halve n before I reach 1?" For n = 8 the answer is 3 (8 → 4 → 2 → 1), so log₂ 8 = 3. Any loop that halves its counter, or doubles it up to n, runs about log₂ n times, so it is O(log n).
def halvings(n):
steps = 0
while n > 1:
n //= 2
steps += 1
return steps
print(halvings(8), halvings(1000), halvings(10**6)) # Output: 3 9 19Compare that with i += 2, which only takes n/2 steps: still O(n). Only multiplying or dividing the counter gives a logarithm.
i = 1
count = 0
while i < 1000:
i *= 2
count += 1
print(count) # Output: 10Recursion trees
A recurrence is an equation that describes the cost of a recursive function in terms of smaller calls. T(n) means "the time for input size n". To solve one, draw a recursion tree: each call is a box, its children are the calls it makes. Add up the work done on each level, then multiply by (or add up over) the number of levels.
- T(n) = T(n/2) + 1, like binary search: one call per level, constant work each, and n halves until 1, so about log₂ n levels. Total O(log n).
- T(n) = 2T(n/2) + n, like merge sort: level 0 has one call doing n work, level 1 has two calls doing n/2 each (n in total), level 2 has four calls doing n/4 each (n again). Every level does n work and there are log₂ n levels, so O(n log n).
- T(n) = 2T(n − 1) + 1, like naive Fibonacci or Tower of Hanoi: the number of calls doubles at every level and there are n levels, so about 2ⁿ calls: O(2ⁿ).
The Master theorem is a shortcut for recurrences of the form T(n) = a·T(n/b) + f(n): a calls, each on 1/b of the input, plus f(n) work to split and combine. Compare f(n) with n^(log_b a). If f(n) is clearly smaller, the answer is n^(log_b a); if they are the same size, multiply by log n; if f(n) is clearly bigger, the answer is f(n). For binary search, a = 1 and b = 2 give n⁰ = 1, the same as f(n) = 1, so O(log n). For merge sort, a = 2 and b = 2 give n¹, the same as f(n) = n, so O(n log n). You rarely need more than this in an interview.
Common mistakes
- Saying "two nested loops, so O(n²)" without checking how far the inner loop runs. Fix: if the inner loop halves or doubles, it is O(n log n); look at the exact bounds.
- Treating i += 2 or i += 100 as logarithmic. Fix: adding a constant is still linear; only multiplying or dividing gives log n.
- Thinking naive recursive Fibonacci is O(n) because n goes down by 1. Fix: each call makes two calls, so the tree doubles at each level: O(2ⁿ).
- Quoting the average case as if it were guaranteed. Fix: say both, for example "O(n log n) on average, O(n²) in the worst case".
- Forgetting work done outside the recursive calls, such as slicing or merging. Fix: that is the f(n) part, and it changes the answer.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Best, average and worst case; analysing loops and recursion · 1/8
This lesson is about cases, halving loops and recursion trees: the tools for working out the cost of any code you write.
Prefer a video by a teacher? Find free videos on “recursion tree method master theorem time complexity explained” on YouTube ↗
In real life: Guessing a roll number in a sorted register
A college office keeps a printed register of 1,000 students sorted by roll number. A clerk who turns pages one by one may need 1,000 looks in the worst case. A clerk who opens the middle and keeps halving needs at most 10 looks, because 2¹⁰ = 1,024 is more than 1,000. The best case for both is one look; the worst case is what decides who goes home on time.
def looks_needed(n):
looks = 0
while n > 0:
n //= 2
looks += 1
return looks
print(looks_needed(1000)) # halving clerk, worst case
print(looks_needed(1000000))Practice (0/3 done)
Write count_halvings(n) for a whole number n ≥ 1. It returns how many times you can do n = n // 2 before n becomes 1. This equals floor(log₂ n). Use a loop, not the math module. Example: count_halvings(8) returns 3 and count_halvings(10) returns 3.
A guessing game: a secret whole number lies between low and high (both included). Each guess is the middle, (low + high) // 2. If the guess is too small, set low = guess + 1; if too big, set high = guess - 1. Write guess_steps(low, high, secret) that returns how many guesses are made, counting the correct one. Assume low ≤ secret ≤ high. Example: guess_steps(1, 100, 50) returns 1.
What is the time complexity of this code? i = n while i > 0: for j in range(i): work() # O(1) i //= 2
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- The real cost of Python and JavaScript built-insThinking in Big-O · same module
- Merge sort and quick sortSorting and searching
- Recursion and sorting algorithmsPython Programming · Algorithms, data structures and testing
- Divide and conquerRecursion and backtracking
- Big-O and searchingPython Programming · Algorithms, data structures and testing
- Recursion: functions that call themselvesRecursion and backtracking
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Linear search for a target that is the first item takes one step. Which case is that?
Answer: Best case
The input that makes the code do the least work is the best case.
What does a loop "while i < n: i *= 3" cost, starting at i = 1?
Answer: O(log n)
Multiplying by 3 reaches n after about log₃ n steps, which is O(log n).
T(n) = 2T(n/2) + n. What is T(n)?
Answer: O(n log n)
Every level of the recursion tree does n work and there are log n levels.
Naive recursive Fibonacci, fib(n) = fib(n − 1) + fib(n − 2), grows about how fast?
Answer: Exponentially, about O(2ⁿ)
Each call makes two more calls, so the tree roughly doubles at each level.
Quicksort with a pivot that is always the smallest item does what?
Answer: Degrades to O(n²)
Each split removes only one item, so there are n levels of up to n work each.
What is the WORST-case time of merge sort?Related
Answer: O(n log n)
It always splits in half and merges n items per level, whatever the input.
What is the base case in a recursive function?Related
Answer: The case simple enough to answer without calling the function again
The base case stops the recursion by answering directly.
Why pass lo and hi instead of slicing nums[mid + 1:] in recursive binary search?Related
Answer: Slicing copies the half, adding O(n) work and memory
A slice copies about n/2 items, so the search would cost O(n) instead of O(log n).
From Divide and conquer
A loop inside a loop, both over the same list of n items, is usually...Related
Answer: O(n^2)
For each of the n items, the inner loop does n steps: n times n.
From Big-O and searching
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 1 more topic in “Thinking in Big-O” before its exam: The real cost of Python and JavaScript built-ins.
- Module 1 exam: Thinking in Big-O · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Arrays and strings. 61 topics left in the course before the final exam.