DEV Community

Cover image for Building Intuition for Big-O by Tracing Code Line by Line
Ameer Abdullah
Ameer Abdullah

Posted on

Building Intuition for Big-O by Tracing Code Line by Line

Building Intuition for Big-O by Tracing Code Line by Line

Complexity analysis becomes intuitive when you count operations by actually tracing through examples. Practice output prediction problems at PyCodeIt.

Most Big-O explanations are abstract. This one is not.

Complexity analysis is just counting operations. Tracing code to count operations is the most reliable way to build genuine intuition for why an algorithm has the complexity it has.


Counting Operations in a Simple Loop

def find_max(lst):
    max_val = lst[0]          # 1 operation
    for item in lst:          # n iterations
        if item > max_val:    # 1 comparison per iteration
            max_val = item    # at most 1 assignment per iteration
    return max_val            # 1 operation
Enter fullscreen mode Exit fullscreen mode

Total operations: 1 + n*2 + 1 = 2n + 2

As n grows large, the constant 2 and the coefficient 2 become irrelevant compared to n. We keep only the dominant term and drop constants: O(n).


Counting a Nested Loop

def has_duplicate(lst):
    for i in range(len(lst)):        # n iterations
        for j in range(len(lst)):    # n iterations for each i
            if i != j and lst[i] == lst[j]:
                return True
    return False
Enter fullscreen mode Exit fullscreen mode

The outer loop runs n times. For each outer iteration, the inner loop runs n times. Total comparisons: n * n = n squared.

Time complexity: O(n squared).

Space complexity: O(1) — no additional data structure is created regardless of input size.


Why Dictionary Lookup Is O(1)

A list lookup lst[i] is O(1) because the index is an offset from the start of the array in memory.

A dictionary lookup d[key] is O(1) on average because Python computes hash(key) to find the bucket where the value is stored, then checks equality. This is a fixed number of operations regardless of dictionary size.

def count_words(text):
    counts = {}
    for word in text.split():           # n iterations
        if word in counts:              # O(1) dictionary lookup
            counts[word] += 1          # O(1) update
        else:
            counts[word] = 1           # O(1) insert
    return counts
Enter fullscreen mode Exit fullscreen mode

Total: n * O(1) = O(n)

Compare to a list-based approach:

def count_words_slow(text):
    words = text.split()
    counts = []
    for word in words:                  # n iterations
        found = False
        for entry in counts:            # up to n iterations
            if entry[0] == word:
                entry[1] += 1
                found = True
                break
        if not found:
            counts.append([word, 1])
    return counts
Enter fullscreen mode Exit fullscreen mode

Total: n * n = O(n squared)

The dictionary version is faster not because of clever algorithm design but because of the O(1) hash table lookup replacing an O(n) linear scan.


Recognizing Complexity From Structure

Once you have traced enough examples, you start recognizing complexity from structure without counting every operation:

  • Single loop over input: O(n)

  • Two nested loops over input: O(n squared)

  • Loop that halves input each iteration: O(log n)

  • Loop over input with a dictionary operation inside: O(n)

  • Recursive function that makes two calls with smaller input: O(2 to the n) if no memoization, O(n) with memoization


The Space Complexity Question

def get_evens(lst):
    return [x for x in lst if x % 2 == 0]
Enter fullscreen mode Exit fullscreen mode

Time: O(n) —> iterates over every element once Space: O(n) — creates a new list that could be up to size n

def has_even(lst):
    return any(x % 2 == 0 for x in lst)
Enter fullscreen mode Exit fullscreen mode

Time: O(n) worst case — might iterate over every element Space: O(1) , the generator expression produces values one at a time without storing them

Interview questions about space complexity often involve identifying whether a solution creates data structures proportional to input size or operates with constant extra space.

Practice output prediction problems at PyCodeIt.


Top comments (0)