OLogN Logarithmic Time

4 min read
Foundational2 min read
Rapid overview

OLogN Logarithmic Time

TL;DR

O(log n) logarithmic time means each step throws away a constant fraction of the remaining work — doubling the data only adds one more step. This is the regime of binary search, balanced trees (SortedSet, SortedDictionary) and B-tree database indexes, which is why a billion-row indexed lookup finishes in roughly thirty comparisons instead of a billion.

How it works

O(log n) — Logarithmic Time

"Operation time grows logarithmically—doubling input size adds only one more step."

❌ Bad example:

public class PriceLookup
{
    private List<PriceLevel> _prices = new(); // unsorted

    public decimal FindPriceNearTarget(decimal target)
    {
        // O(n) - must check every price
        return _prices
            .Select(p => p.Price)
            .OrderBy(p => Math.Abs(p - target))
            .First();
    }
}

// Searching 1 million prices requires checking all
var price = lookup.FindPriceNearTarget(150.00m);

Linear search doesn't exploit any structure—wasteful for large datasets.

✅ Good example:

public class PriceLookup
{
    private List<PriceLevel> _prices = new(); // sorted

    public decimal FindPriceNearTarget(decimal target)
    {
        // O(log n) - binary search
        int index = _prices.BinarySearch(new PriceLevel { Price = target });
        if (index < 0) index = ~index; // insertion point

        // Check nearby prices
        return _prices[Math.Max(0, index - 1)].Price;
    }

    public void AddPrice(PriceLevel price)
    {
        int index = _prices.BinarySearch(price);
        if (index < 0) index = ~index;
        _prices.Insert(index, price); // O(n) insert, but search is O(log n)
    }
}

// Searching 1 million sorted prices takes ~20 comparisons
var price = lookup.FindPriceNearTarget(150.00m);

👉 Binary search halves the search space each step: O(log₂ n).

🔥 SortedSet for automatic ordering:

public class OrderPriceIndex
{
    private SortedSet<decimal> _buyPrices = new();
    private SortedSet<decimal> _sellPrices = new();

    public void AddBuyOrder(decimal price)
    {
        // O(log n) - balanced tree insert
        _buyPrices.Add(price);
    }

    public decimal GetBestBid()
    {
        // O(log n) - find max in tree
        return _buyPrices.Max;
    }

    public IEnumerable<decimal> GetPricesInRange(decimal min, decimal max)
    {
        // O(log n + k) - where k is result count
        return _buyPrices.GetViewBetween(min, max);
    }
}

👉 SortedSet (red-black tree) maintains order with O(log n) operations.

🔥 Dictionary resizing:

public class TradeCache
{
    // Dictionary internal resizing is O(n), but happens O(log n) times
    // as capacity doubles: 4, 8, 16, 32, 64...
    private Dictionary<int, Trade> _trades = new();

    public void AddTrade(Trade trade)
    {
        // Amortized O(1), but understanding the log n resize pattern is key
        _trades[trade.Id] = trade;
    }
}

👉 Doubling strategy means O(log n) resizes over n insertions.

💡 In trading systems:

  • Use binary search for finding price levels in sorted order books.
  • SortedSet for maintaining best bid/ask with efficient range queries.
  • B-tree indexes in databases provide O(log n) lookups for billions of records.

Quick recall Q&A

Q: Why is binary search O(log n)?

Each comparison eliminates half the remaining elements. For n=1,000,000: step 1 = 500,000, step 2 = 250,000, ..., step 20 = 1. That's log₂(1,000,000) ≈ 20 steps.

Q: What data structures provide O(log n) operations?

Balanced trees (red-black, AVL), SortedSet, SortedDictionary, B-trees (database indexes), heaps (priority queues for min/max).

Q: Is SortedDictionary better than Dictionary?

Depends. Dictionary is O(1) average for lookup, SortedDictionary is O(log n). Use SortedDictionary when you need ordered keys or range queries.

Q: What's the complexity of List.BinarySearch()?

O(log n), but list must be sorted first. Sorting is O(n log n), so binary search only makes sense for multiple queries on static data.

Q: How does O(log n) compare to O(1)?

O(1) is faster, but O(log n) is still very fast. log₂(1 billion) ≈ 30. For practical purposes, O(log n) scales well even for massive datasets.

Q: Can O(log n) be better than O(1) in practice?

Rarely, but yes. SortedSet uses less memory than Dictionary for sparse data, and cache locality can make tree traversal competitive with hash table lookups.

Q: What's the complexity of finding min/max in SortedSet?

O(log n) for Min/Max. Internally it traverses left/right to leaf nodes. For frequent min/max, use a heap (O(1) peek, O(log n) removal).

Q: How does database indexing relate to O(log n)?

B-tree indexes enable O(log n) lookups. A table with 1 billion rows and a B-tree index (depth ~4) needs only 4 disk seeks for a lookup.

Q: What's the difference between O(log n) and O(n)?

Massive. O(log n) for n=1,000,000 is ~20 steps. O(n) is 1,000,000 steps. Logarithmic algorithms scale dramatically better.

Q: Can loops be O(log n)?

Yes, if the loop variable doubles/halves each iteration. Example: for (int i = 1; i < n; i *= 2) runs log₂(n) times.

See also