Chapter 05 of 32

Big O, Big Ω & Big Θ

When we study the efficiency of an algorithm, we need a way to describe how its running time grows as the input size increases.

That's where asymptotic notation comes in.

The three most common notations are:

Big O     → O
Big Omega → Ω
Big Theta → Θ

They look similar, but they describe different things about an algorithm's growth.

genui{"learning_viz":{"type_id":"BIG_O_TIME_COMPLEXITY"}}

Big O — O

Big O notation describes an upper bound on an algorithm's growth.

In simple words, it tells us how much work an algorithm can take at most, asymptotically.

This is why Big O is commonly used when talking about the worst-case complexity of an algorithm.

For example, consider searching for a number in an unsorted array:

int[] numbers = {10, 20, 30, 40, 50};

for (int number : numbers) {
    if (number == 50) {
        System.out.println("Found");
        break;
    }
}

If the number we're looking for is the last element, we may need to check every element.

If there are n elements, we may perform up to n checks.

So the upper bound is:

O(n)

We can say the algorithm is O(n).

Why is Big O Important?

Big O helps us understand how an algorithm behaves when the input becomes very large.

For example:

O(1)       → Constant
O(log n)   → Logarithmic
O(n)       → Linear
O(n log n) → Linearithmic
O(n²)      → Quadratic
O(2ⁿ)      → Exponential

An O(n) algorithm generally scales much better than an O(n²) algorithm when n becomes very large.

Big Ω — Omega

Big Omega describes a lower bound on an algorithm's growth.

In simpler words, it tells us the minimum amount of work an algorithm needs to perform asymptotically.

For example, imagine we're searching for a number in an array:

10, 20, 30, 40, 50

If we're searching for 10, we find it immediately.

We only need one comparison.

So the best-case behavior is:

Ω(1)

This means the algorithm has a lower bound of constant time for this situation.

Remember:

Big O → upper bound

Big Ω → lower bound

Big Θ — Theta

Big Theta describes a tight bound.

In simple words, it tells us that the algorithm grows at the same rate from both the upper and lower sides.

For example, consider this loop:

for (int i = 0; i < n; i++) {
    System.out.println(i);
}

The loop always runs exactly n times.

It doesn't matter what values are inside the input. The loop itself always performs n iterations.

So we can describe its growth as:

Θ(n)

We can think of this as:

Lower bound → Θ(n) → Upper bound

The algorithm's growth is tightly bounded by n.

A Simple Way to Remember Them

Think about the three notations like this:

Big O
"What is the upper limit of growth?"

Big Ω
"What is the lower limit of growth?"

Big Θ
"What is the exact asymptotic growth rate?"

This is a simplified way of remembering them, but it's very useful when you're starting out.

Example: Finding an Element

Let's take the common linear search example.

int[] numbers = {10, 20, 30, 40, 50};

Suppose we're searching for 50.

The algorithm might check:

10 → 20 → 30 → 40 → 50

It checks all n elements.

So the worst-case complexity is:

O(n)

But if we're searching for 10:

10

we find it immediately.

So the best-case complexity is:

Ω(1)

For linear search, we therefore commonly say:

Best Case    → Ω(1)
Worst Case   → O(n)

The exact average-case analysis depends on assumptions about where the target occurs.

Example: A Simple Loop

Consider:

for (int i = 0; i < n; i++) {
    System.out.println(i);
}

This loop always executes n times.

So:

Upper bound → O(n)
Lower bound → Ω(n)
Tight bound  → Θ(n)

Because both the upper and lower bounds grow linearly, we can confidently say:

Θ(n)

Another Example: Nested Loops

Consider:

for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
        System.out.println(i + j);
    }
}

The outer loop runs n times.

For every iteration of the outer loop, the inner loop also runs n times.

So the total number of operations is approximately:

n × n = n²

Therefore:

Θ(n²)

And consequently, the algorithm is also:

O(n²)

and:

Ω(n²)

because its growth is tightly bounded by .

Big O Does Not Always Mean Worst Case

This is an important point.

You'll often hear people say:

"Big O means worst case."

That's a useful shortcut when you're learning DSA, but technically Big O describes an upper bound, not specifically the worst case.

Similarly:

  • O → upper bound

  • Ω → lower bound

  • Θ → tight bound

Whether you're talking about the best case, worst case, or average case depends on what behavior you're analyzing.

Comparing the Three

Let's use an algorithm whose running time grows linearly.

Its bounds could be:

O(n)
Ω(n)
Θ(n)

Because its actual growth is tightly linear, all three descriptions can apply.

But for an algorithm such as linear search, different cases can have different complexities:

Best case  → Ω(1)
Worst case → O(n)

The important thing is to understand what bound you're describing.

Why Do We Ignore Constants?

Suppose an algorithm performs:

3n + 5

operations.

When discussing asymptotic growth, we focus on the dominant growth term.

So:

3n + 5

is considered:

Θ(n)

Similarly:

5n² + 10n + 20

is:

Θ(n²)

We care about how the function grows when n becomes very large.

A Real-Life Example

Imagine you're checking students' names to find John.

If John happens to be the first student, you find him immediately.

That's like the lower bound:

Ω(1)

If John is the last student, you might have to check everyone.

That's like the upper bound:

O(n)

If an algorithm always requires roughly n steps regardless of the input arrangement, then we can describe its tight growth as:

Θ(n)

This way of thinking makes the three notations easier to understand.

The Main Idea

The three important asymptotic notations are:

O(n) → Upper bound
Ω(n) → Lower bound
Θ(n) → Tight bound

Think of them as three different ways of describing how an algorithm grows:

Big O tells us how large the growth can be.

Big Ω tells us how small the growth can be.

Big Θ tells us the actual asymptotic growth when both bounds match.

As you solve more DSA problems, you'll use these notations constantly to analyze and compare algorithms.