Problem Solving · #9 of 11

Complexity + Memory Model

Big-O, mutability, and copying

Why it matters

Speed and space are what separate accepted from TLE solutions.

The idea

Think in operations per input size. list.append() is O(1) amortised; list.insert(0, x) is O(n) because everything shifts. set / dict lookup is O(1); list membership (x in lst) is O(n).

See O(n²) in action

Bubble sort compares every adjacent pair and bubbles the largest to the end on each pass. Tap Auto and count: 24 elements take ~200 comparisons. Doubling to 48 wouldn't take 2× — it would take ~4×. That's what O(n²) looks like.

comparisons: 0 swaps: 0

Try it

Time the difference between list and set membership:

import time

N = 50000
data = list(range(N))
data_set = set(data)
needles = [N - 1, N // 2, 0]

t0 = time.perf_counter()
hits_list = sum(1 for x in needles if x in data)
t1 = time.perf_counter()
hits_set = sum(1 for x in needles if x in data_set)
t2 = time.perf_counter()

print(f"list lookups: {hits_list}  time: {(t1-t0)*1e6:.1f} µs")
print(f"set  lookups: {hits_set}  time: {(t2-t1)*1e6:.1f} µs")
not loaded

Shallow vs deep copy — mutables surprise people:

import copy

grid = [[0] * 3 for _ in range(3)]
shallow = list(grid)       # same inner lists!
deep = copy.deepcopy(grid)

shallow[0][0] = 99
deep[0][0] = -1

print("grid    :", grid)
print("shallow :", shallow)
print("deep    :", deep)
not loaded

In place mutation vs new object:

# sorted() makes a new list, sort() mutates in place
nums = [3, 1, 4, 1, 5]
asc = sorted(nums)         # new
nums.sort(reverse=True)    # in place
print("asc :", asc)
print("nums:", nums)
not loaded

Quick check

Mini drills

Common mistakes

Key takeaways