algocat.tech
Study notes
Mar 9, 2026 8 min read

Deep dive into O(log n) Time Complexity

A deep dive into O(log n) time complexity: what it really means, why the logarithm's base matters for performance, and where it shows up in everyday algorithms.

Logarithmic time complexity, denoted as O(log n), is a powerful concept in algorithm analysis. It describes algorithms whose runtime increases proportionally to the logarithm of the input size, rather than the input size itself. This makes O(log n) algorithms incredibly efficient for large datasets, but it's crucial to understand that the base of the logarithm, while often suppressed in Big O notation, can significantly impact performance.

What is a Logarithm?

Before diving deep into the complexities of O(log n), let's first understand what a logarithm actually is. In simple terms, a logarithm answers the question: "To what power must I raise this base number to get this other number?"

  • Example: Consider the equation 2³ = 8. Here, 2 is the base, 3 is the exponent (or power), and 8 is the result.

  • Logarithm: The logarithm of 8 to the base 2 is 3. We write this as log₂ 8 = 3.

  • General Form: If bˣ = y, then log<sub>b</sub> y = x.

    • b is the base
    • x is the logarithm (the exponent)
    • y is the number

Common Logarithm Bases:

  • Base 10 (Common Logarithm): Written as log₁₀ or simply log. It answers the question: "To what power must I raise 10 to get this number?" For example, log₁₀ 100 = 2 because 10² = 100.
  • Base 2 (Binary Logarithm): Written as log₂. It answers the question: "To what power must I raise 2 to get this number?" For example, log₂ 8 = 3 because 2³ = 8. Binary logarithms are very common in computer science due to the binary nature of computers.
  • Base e (Natural Logarithm): Written as ln. 'e' is a special mathematical constant approximately equal to 2.71828.

Why Logarithms Matter for Time Complexity:

Logarithmic time complexity arises when an algorithm repeatedly divides the problem size in each step. Think of it as the inverse of exponential growth. If an algorithm halves the input size at each step, the number of steps required to process the data grows logarithmically. This is because the number of times you can divide a number by 2 before reaching 1 is proportional to the logarithm (base 2) of that number.

Intuition Behind O(log n): Divide and Conquer

The core intuition behind logarithmic time complexity is the principle of "divide and conquer." Algorithms with O(log n) time complexity typically solve problems by:

  1. Dividing: The problem is broken down into smaller subproblems.
  2. Conquering: One of the subproblems is solved directly.
  3. Combining: The result from the solved subproblem is used to deduce the overall solution (often trivially).

The key here is that in each step, the problem size is effectively reduced by a constant factor. This repeated division leads to the logarithmic behavior.

Formal Definition & Base Implications

Mathematically, O(log n) means that the number of operations the algorithm performs is proportional to the logarithm of the input size 'n'. The base of the logarithm is often omitted in Big O notation because different logarithm bases are related by a constant factor:

log₂ n = (log₁₀ n) / (log₁₀ 2)

Since Big O notation focuses on the growth rate and ignores constant factors, the base of the logarithm doesn't fundamentally change the classification as O(log n). However, the base becomes critically important when considering actual performance. While an algorithm might be O(log n) regardless of the base, the algorithm with the smaller base will perform fewer operations.

Examples of O(log n) Algorithms

  1. Binary Search (Base 2):

    • Problem: Searching for a specific element in a sorted array.
    • Algorithm: Binary search repeatedly divides the search interval in half.
    • Code (Python):
      def binary_search(arr, target):
       low = 0
       high = len(arr) - 1
      
       while low <= high:
           mid = (low + high) // 2  # Integer division
      
           if arr[mid] == target:
               return mid  # Target found at index mid
           elif arr[mid] < target:
               low = mid + 1  # Search in the right half
           else:
               high = mid - 1  # Search in the left half
      
       return -1  # Target not found
      
    • Time Complexity Analysis: In each iteration, the search space is halved. Therefore, the number of iterations required is proportional to log₂ n, where 'n' is the size of the array. Hence, binary search has a time complexity of O(log n). Here, the base is inherently 2 due to the halving.
    • Example: Suppose arr = [2, 5, 7, 8, 11, 12] and target = 13. The binary search would go through the following steps:
      • low = 0, high = 5, mid = 2, arr[mid] = 7. Since 13 > 7, low = 3.
      • low = 3, high = 5, mid = 4, arr[mid] = 11. Since 13 > 11, low = 5.
      • low = 5, high = 5, mid = 5, arr[mid] = 12. Since 13 > 12, low = 6.
      • low = 6, high = 5. The loop terminates because low > high. The function returns -1 (not found).
  2. B-Trees (Base Determined by Branching Factor):

    • Problem: Efficiently storing and retrieving large amounts of data on disk, commonly used in databases and file systems.

    • Algorithm: B-Trees are self-balancing tree data structures where each node can have multiple children (unlike binary trees which have at most two). The key idea is to minimize disk I/O operations because disk access is much slower than memory access.

    • How Logarithms Appear: The height of a B-Tree is logarithmic with respect to the number of nodes. The height is approximately log<sub>b</sub> N, where N is the number of nodes and 'b' is the branching factor (the maximum number of children a node can have).

    • Why the Base Matters (Significant Performance Impact): A larger branching factor means a shorter tree, which translates to fewer disk accesses to find a specific key.

    • Example: Consider a database index with 1 million records:

      • Binary Search Tree (BST): Has a maximum branching factor of 2. A perfectly balanced BST might have a height close to log₂ (1,000,000) ≈ 20. This means up to 20 disk I/O operations to find a record.
      • B-Tree with Branching Factor 100: Can have up to 100 children per node. A B-Tree with the same data might have a height closer to log₁₀₀ (1,000,000) = 3. This dramatically reduces the number of disk accesses to just 3.
    • Time Complexity Analysis: While the Big O time complexity for search (O(log n)) is the same for both, the actual runtime can be drastically different. The base (branching factor) heavily influences the constant factor hidden within the O notation. Choosing a large branching factor, appropriate for the disk block size, minimizes the number of expensive disk I/Os. This is why B-Trees are essential for database performance.

  3. Exponentiation by Squaring:

    • Problem: Calculating x^n efficiently.

    • Algorithm: This technique leverages the property that x^n = (x^(n/2))^2 if n is even, and x^n = x * (x^((n-1)/2))^2 if n is odd. It recursively breaks down the exponent into smaller powers.

    • Code (Python):

      def power_optimized(x, n):
          if n == 0:
              return 1
          y = power_optimized(x, n // 2)
          if n % 2 == 0:
              return y * y
          else:
              return x * y * y
      
    • Time Complexity Analysis: The exponent n is effectively halved in each recursive call. The number of recursive calls is therefore proportional to log₂ n. Hence, the time complexity is O(log n).

    • Example: To calculate 2¹⁰, the algorithm would perform the following steps:

      1. power(2, 10) -> y = power(2, 5) -> y * y
      2. power(2, 5) -> 2 * power(2, 2) * power(2,2) (using optimized example)
      3. power(2, 2) -> y = power(2, 1) -> y * y
      4. power(2, 1) -> 2 * power(2, 0) * power(2,0) (using optimized example)
      5. power(2, 0) -> 1

      The total number of recursive calls is related to the logarithm of 10.

Why O(log n) is Important

O(log n) algorithms are extremely efficient, especially when dealing with large datasets. They offer a significant advantage over linear or quadratic algorithms. The choice of base, while abstracted away in Big O, can be a critical performance factor, as demonstrated by the B-Tree example.

Limitations and Considerations

  • Data Structure & Sorted Data Requirement: Many O(log n) algorithms, like binary search, rely on sorted data or specific data structures like balanced trees. The overhead of maintaining these structures (e.g., balancing a BST) or initially sorting the data must be considered.
  • Constant Factors: While Big O notation ignores constant factors, they can still impact the actual runtime, especially for smaller input sizes. An algorithm with a smaller constant factor might outperform an O(log n) algorithm for small 'n'.
  • Space Complexity: Some logarithmic algorithms might have a higher space complexity due to recursive calls or the storage requirements of specific data structures.

Conclusion

Logarithmic time complexity (O(log n)) is a cornerstone of efficient algorithm design. Understanding the divide-and-conquer principle is essential. Furthermore, recognizing that the base of the logarithm, while often omitted, can have a substantial impact on real-world performance, especially in scenarios like database indexing with B-Trees, is crucial for building optimized and scalable systems. By carefully selecting algorithms and data structures that leverage logarithmic time complexity and considering the practical implications of the base, you can achieve significant performance improvements in your applications.

More like this