Skip to main content
โšก Calmops

Understanding Big O Notation

Introduction

Big O notation describes algorithm efficiency. Understanding it helps you write faster code and choose the right approach. This guide covers Big O fundamentals with examples.

What Is Big O

Definition

Big O describes how runtime scales with input size:

O(n) = "order n"
O(nยฒ) = "order n squared"

Why It Matters

  • Predict performance
  • Compare algorithms
  • Optimize code
  • Pass interviews

Common Complexities

O(1) - Constant

# Always one operation
def first_element(arr):
    return arr[0]

O(log n) - Logarithmic

# Binary search
def binary_search(arr, target):
    left, right = 0, len(arr) - 1
    
    while left <= right:
        mid = (left + right) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    
    return -1

O(n) - Linear

# Single loop
def find_max(arr):
    max_val = arr[0]
    for num in arr:
        if num > max_val:
            max_val = num
    return max_val

O(n log n) - Linearithmic

# Sorting: merge sort, heap sort
# Most efficient comparison sorts
def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    
    return merge(left, right)

O(nยฒ) - Quadratic

# Nested loops
def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]

O(2^n) - Exponential

# Recursive without memoization
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

O(n!) - Factorial

# Permutations
def permutations(arr):
    if len(arr) <= 1:
        return [arr]
    
    result = []
    for i, element in enumerate(arr):
        remaining = arr[:i] + arr[i+1:]
        for p in permutations(remaining):
            result.append([element] + p)
    
    return result

Visual Comparison

Complexity 10 100 1000
O(1) 1 1 1
O(log n) 3 7 10
O(n) 10 100 1000
O(n log n) 30 700 10,000
O(nยฒ) 100 10,000 1,000,000
O(2^n) 1024 2^100 2^1000

Space Complexity

O(1) - Constant

def sum_arr(arr):
    total = 0  # One variable
    for num in arr:
        total += num
    return total

O(n) - Linear

def double_arr(arr):
    return [x * 2 for x in arr]  # New array

Analyzing Code

Rules

  1. Drop constants
  2. Drop lower terms
  3. Different inputs = different variables

Examples

# O(n) + O(n) = O(n)
def example1(n):
    for i in range(n):
        print(i)
    for i in range(n):
        print(i)

# O(nยฒ) + O(n) = O(nยฒ)
def example2(n):
    for i in range(n):
        for j in range(n):
            print(i, j)
    for i in range(n):
        print(i)

# O(1) inside O(n) = O(n)
def example3(n):
    for i in range(n):
        x = i * 2  # O(1)

Common Patterns

Loops

  • Single loop: O(n)
  • Two nested loops: O(nยฒ)
  • Loop with halving: O(log n)

Recursion

  • Single call: O(n)
  • Binary tree recursion: O(2^n)

Data Structures

Operation Array Linked List Hash Table
Access O(1) O(n) O(1)
Search O(n) O(n) O(1)
Insert O(n) O(1) O(1)
Delete O(n) O(1) O(1)

Practical Application

Choose Algorithms

Input Size Use
< 1000 O(nยฒ) okay
< 100K O(n log n)
< 10M O(n)
10M+ O(log n), O(1)

Optimization

# Before: O(nยฒ)
def has_duplicates_bad(arr):
    for i in range(len(arr)):
        for j in range(i+1, len(arr)):
            if arr[i] == arr[j]:
                return True
    return False

# After: O(n)
def has_duplicates_good(arr):
    seen = set()
    for num in arr:
        if num in seen:
            return True
        seen.add(num)
    return False

Conclusion

Big O helps you reason about efficiency. Focus on dominant factors, drop constants, and think about how algorithms scale. Practice analyzing code to build intuition.


Resources

Comments