Back to Course
Fundamentals

Complexity Analysis of Data Structures and Algorithms

Complexity Analysis is the process of determining how the runtime or memory usage of an algorithm scales with the size of the input. It is a fundamental tool for evaluating algorithm efficiency and comparing different approaches to solving a problem.

Why Complexity Analysis Matters

  • Scalability: Helps predict how an algorithm performs as input size grows.
  • Comparison: Enables objective comparison between different algorithms.
  • Resource Planning: Helps estimate time and memory requirements for real-world applications.
  • Optimization: Identifies performance bottlenecks and guides optimization efforts.

Types of Complexity

1. Time Complexity

The amount of time an algorithm takes to run as a function of the input size n. Measured in terms of operations or steps, not clock time.

2. Space Complexity

The amount of memory an algorithm uses as a function of the input size n. Includes:

  • Fixed part: Memory for constants, program code, and simple variables.
  • Variable part: Memory for data structures, recursion stack, and dynamically allocated memory.

Asymptotic Notations

Asymptotic notations describe the behavior of an algorithm for large input sizes, ignoring constant factors and lower-order terms.

1. Big O Notation (O) — Upper Bound

Describes the worst-case complexity. It gives an upper bound on the growth rate of an algorithm.

f(n) = O(g(n)) if f(n) ≤ c · g(n) for all n ≥ n₀ (c > 0)

Example: If an algorithm takes at most 2n + 5 operations, its time complexity is O(n).

2. Big Omega Notation (Ω) — Lower Bound

Describes the best-case complexity. It gives a lower bound on the growth rate.

f(n) = Ω(g(n)) if f(n) ≥ c · g(n) for all n ≥ n₀ (c > 0)

Example: If an algorithm takes at least n operations, its complexity is Ω(n).

3. Big Theta Notation (Θ) — Tight Bound

Describes the average-case complexity or when the upper and lower bounds match.

f(n) = Θ(g(n)) if c₁ · g(n) ≤ f(n) ≤ c₂ · g(n) for all n ≥ n₀ (c₁, c₂ > 0)

Example: If an algorithm takes exactly n operations, its complexity is Θ(n).

Common Complexity Classes

NotationNameGrowth RateExample Algorithms
O(1)ConstantIndependent of input sizeArray access, hash table lookup
O(log n)LogarithmicIncreases slowlyBinary search, BST operations
O(n)LinearProportional to input sizeLinear search, array traversal
O(n log n)Linearithmicn times log nMerge sort, Quick sort (avg)
O(n²)Quadraticn squaredBubble sort, Selection sort
O(2ⁿ)ExponentialDoubles with each inputNaive Fibonacci, subset sum
O(n!)Factorialn factorialTraveling salesman (brute force)

Visualizing Growth Rates

For n = 100:

  • O(1): 1 operation
  • O(log n): ~7 operations
  • O(n): 100 operations
  • O(n log n): ~664 operations
  • O(n²): 10,000 operations
  • O(2ⁿ): 1.27 × 10³⁰ operations (impossible for large n)

Analyzing Complexity with Examples

Example 1: Constant Time — O(1)

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

# Time: O(1), Space: O(1)

Example 2: Linear Time — O(n)

def linear_time(arr):
    total = 0
    for x in arr:
        total += x
    return total

# Time: O(n), Space: O(1)

Example 3: Quadratic Time — O(n²)

def quadratic_time(arr):
    for i in range(len(arr)):
        for j in range(len(arr)):
            print(arr[i], arr[j])

# Time: O(n²), Space: O(1)

Example 4: Logarithmic Time — O(log n)

def logarithmic_time(arr, target):
    low, high = 0, len(arr) - 1
    while low <= high:
        mid = (low + high) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            low = mid + 1
        else:
            high = mid - 1
    return -1

# Time: O(log n), Space: O(1)

Example 5: Linearithmic Time — O(n log n)

def linearithmic_time(arr):
    arr.sort()  # Python uses Timsort (O(n log n))
    return arr

# Time: O(n log n), Space: O(n) for Timsort

Example 6: Exponential Time — O(2ⁿ)

def fibonacci_recursive(n):
    if n <= 1:
        return n
    return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2)

# Time: O(2ⁿ), Space: O(n) (call stack)

Complexity of Common Data Structures

Data StructureAccessSearchInsertionDeletion
ArrayO(1)O(n)O(n)O(n)
Sorted ArrayO(1)O(log n)O(n)O(n)
Linked ListO(n)O(n)O(1)*O(1)*
StackO(n)O(n)O(1)O(1)
QueueO(n)O(n)O(1)O(1)
BST (balanced)O(log n)O(log n)O(log n)O(log n)
BST (unbalanced)O(n)O(n)O(n)O(n)
AVL TreeO(log n)O(log n)O(log n)O(log n)
B-TreeO(log n)O(log n)O(log n)O(log n)
Hash TableO(1) avgO(1) avgO(1) avgO(1) avg
HeapO(1) min/maxO(1) min/maxO(log n)O(log n)

* At beginning of list (with head pointer).

Complexity of Common Sorting Algorithms

AlgorithmBest CaseAverage CaseWorst CaseSpace
Bubble SortO(n)O(n²)O(n²)O(1)
Selection SortO(n²)O(n²)O(n²)O(1)
Insertion SortO(n)O(n²)O(n²)O(1)
Merge SortO(n log n)O(n log n)O(n log n)O(n)
Quick SortO(n log n)O(n log n)O(n²)O(log n)
Heap SortO(n log n)O(n log n)O(n log n)O(1)
Counting SortO(n + k)O(n + k)O(n + k)O(k)
Radix SortO(nk)O(nk)O(nk)O(n + k)

Tips for Analyzing Complexity

  • Ignore constants: O(2n) is O(n), O(100) is O(1).
  • Focus on the dominant term: O(n² + n) is O(n²).
  • Multiple inputs: If an algorithm takes two inputs, use O(n + m) or O(n × m).
  • Recursive functions: Analyze the recurrence relation.
  • Auxiliary space: Only consider additional memory, not the input itself.
  • Worst-case vs average-case: Consider which is more relevant for your use case.

Common Pitfalls

  • Ignoring space complexity: Memory constraints can be as important as time.
  • Overlooking hidden costs: Function calls, data copying, and I/O operations.
  • Assuming average-case: In critical applications, worst-case is often more important.
  • Not considering constants: For small n, constants can matter.

Key Takeaways

  • Complexity analysis is essential for understanding and comparing algorithms.
  • Time complexity measures runtime; space complexity measures memory usage.
  • Big O (upper bound), Big Ω (lower bound), and Big Θ (tight bound) are the three main notations.
  • Common complexity classes include O(1), O(log n), O(n), O(n log n), O(n²), O(2ⁿ), and O(n!).
  • Understanding complexity helps in choosing the right algorithm for your needs.

Ready to master Data Structures & Algorithms?

Learn DSA hands-on with mentor-led sessions, real interview practice, and placement support.

Explore Course