Why your code is slow (or fast)?
Imagine you need to sort a billion names for a search engine. There are dozens of ways to do it. Each one works. But some finish in seconds and others take hours. Algorithm analysis helps you compare them.
We care most about running time. Memory matters too, but time is what makes or breaks a program. An algorithm that takes too long is useless, no matter how much memory you have.
But it runs fast on my machine!
You could run two algorithms on your laptop and compare. But that result means nothing outside your machine. A fast algorithm on a high-end server might crawl on a budget PC. Hardware varies. Programming languages vary. Even coding style affects execution time.
We need a measure that is tied to the algorithm itself, not the machine running it.
That measure is input size: . It is the number of elements you are working with. The length of an array. The nodes in a graph. The bits in a number. Bigger means more work. If you express running time as a function of , you get a machine-independent measure. Call it .
What is in ?
counts operations. Basic steps like adding numbers, comparing values, or reading memory.
Suppose you are summing a list of numbers. You loop through each one and add it. That is roughly operations. So .
Now imagine sorting that list with Bubble Sort. You compare pairs of numbers in nested loops. That leads to roughly comparisons, plus some setup work. So .
Each term reflects a part of the algorithm. The comes from the nested loops. The might be a single loop for swaps. The is fixed setup work.
We count these operations on an idealized machine where each step takes the same time. This removes real-world noise like slow CPUs or cache delays. The focus stays on the algorithm's logic.
Inputs are not always the same
The same algorithm can behave very differently depending on the input.
A sorted list might let a sorting algorithm finish early. A reverse-sorted list forces it to do every step. That is why we analyze three cases:
- Best Case: The luckiest input. Linear search finds the item first. That is 1 step.
- Worst Case: The hardest input. Linear search checks every element. That is steps.
- Average Case: A typical random input. Linear search checks about half the list. That is roughly steps.
The worst case is the most useful. It guarantees performance no matter what input arrives. The average case predicts real-world behavior but requires assumptions about input patterns. The best case is rarely the one you plan for.
Simplifying
For large inputs, not all terms in matter equally.
Take . At , the term is a trillion. The term is four million. The constant is negligible. The term dominates everything else.
This is the idea of rate of growth. It describes how running time scales as input grows. Computer scientists call this time complexity.
For large :
We focus on the dominant term because it determines the algorithm's behavior at scale.
Asymptotic notation
We use asymptotic notation to describe rate of growth cleanly, without carrying all the terms.
A function is asymptotic to if:
This means and grow at the same rate for very large . We use as a simpler approximation of .
For , a natural choice is . But we could also pick , which grows faster and gives an upper estimate. Or , which grows slower and gives a lower estimate.
These choices lead to three notations: Big-, Big-, and Big-.
Big-: the upper bound
Big- sets a ceiling. It says the function will never grow faster than this.
Formally, if there exist constants and such that:
There are infinitely many valid choices for . If , then is valid. So is or . All are correct upper bounds. In practice, we pick the tightest one.
For , the tightest choice is . The smaller terms are dropped. Big- answers the question: how bad can it get?
Big-: the lower bound
Big- sets a floor. It says the function will never grow slower than this.
Formally, if there exist constants and such that:
Again, there are infinitely many valid choices. If , then is valid. So is or . In practice, we pick the largest that still stays below .
For , the tightest choice is . Big- answers the question: how fast can it get?
Big-: the tight bound
Big- gives a ceiling. Big- gives a floor. Big- gives both at once.
Formally, if there exist constants , , and such that:
is squeezed between two multiples of . It grows exactly like , up to constant factors.
For :
- Upper bound:
- Lower bound:
- Together:
Putting it all together
Here is how best, worst, and average cases look for linear search.
Worst case (, item is last or missing):
- : at most linear time.
- : at least linear time.
- : exactly linear time.
Best case (, item is first):
- : at most constant time.
- : at least constant time.
- : exactly constant time.
Average case (, random input):
- : at most linear time.
- : at least linear time.
- : exactly linear time.
Does mean worst case?
Not automatically. says the running time grows no faster than linear. It says nothing about which case you are analyzing.
For linear search:
- Worst case: , so .
- Best case: , so .
Both are notation. They describe different cases. Whenever you see , ask: best, worst, or average?
So,
- gives an upper bound. It answers: how bad can it get?
- gives a lower bound. It answers: how fast can it get?
- gives a tight bound. It answers: what is the exact growth rate?
- Case analysis and asymptotic notation are independent. You can apply any notation to any case.
- In practice, we focus on worst-case Big-. It guarantees performance under the hardest conditions.