PAITS Cheat Sheet Computer Science Intermediate Updated 2026-10-23

Python Data Structures & Algorithmic Complexity Cheat Sheet

Big-O time/space complexity, collections module, heap/bisect idioms, and amortized analysis.

Executive Summary: A production and technical-interview reference covering CPython internal memory layouts (dynamic over-allocated pointer arrays for list, open-addressing hash tables with compact indices for dict), Big-O time and space guarantees, and idiomatic standard-library patterns.

Key Equations & Invariants

Amortized List Append Cost
T_{\text{amortized}}(n) = \frac{1}{n}\sum_{i=1}^{n} O(1) + \frac{O(n)}{n} = O(1)

CPython over-allocates by roughly 12.5% on resize, making n appends O(n) total.

Hash Table Load Factor Bound
\alpha = \frac{n}{m} \le \frac{2}{3}

CPython resizes dict compact tables when load factor exceeds 2/3 to keep probes O(1).

Binary Heap Parent / Child Indices
\text{parent}(i) = \lfloor (i-1)/2 \rfloor, \quad \text{children}(i) = (2i+1,\; 2i+2)

0-indexed array representation used by Python's heapq module (min-heap).

1. CPython Built-In Container Complexity Matrix

CPython's `list` is a contiguous array of `PyObject*` pointers (`ob_item`), not a linked list. Inserting or popping at index 0 shifts $n-1$ pointers ($O(n)$), whereas `collections.deque` uses a doubly-linked list of fixed-size 64-element blocks for $O(1)$ operations at both ends.

StructureAccess / LookupInsert / AppendDelete / PopSpace
list (dynamic array)O(1) by indexO(1) end / O(n) headO(1) end / O(n) midO(n)
collections.dequeO(n) mid / O(1) endsO(1) append / appendleftO(1) pop / popleftO(n)
dict (compact hash)O(1) avg / O(n) worstO(1) amortizedO(1) amortizedO(n)
set / frozensetO(1) avg (`x in s`)O(1) amortizedO(1) amortizedO(n)
heapq (binary min-heap)O(1) min (`h[0]`)O(log n) `heappush`O(log n) `heappop`O(n)
High-performance Sliding Window Monotonic Queue & Top-K Heap patternspython
from collections import deque, Counter
import heapq

def sliding_window_max(nums: list[int], k: int) -> list[int]:
    """O(n) monotonic decreasing deque storing indices."""
    q: deque[int] = deque()
    out: list[int] = []
    for i, val in enumerate(nums):
        while q and nums[q[-1]] <= val:
            q.pop()
        q.append(i)
        if q[0] <= i - k:
            q.popleft()
        if i >= k - 1:
            out.append(nums[q[0]])
    return out

def top_k_frequent(words: list[str], k: int) -> list[str]:
    counts = Counter(words)
    return heapq.nsmallest(k, counts.keys(), key=lambda w: (-counts[w], w))

2. Binary Search (`bisect`) & Prefix Trees (Tries)

Use `bisect_left` to find the first index where `arr[i] >= target` and `bisect_right` for the first index where `arr[i] > target`. For string prefix matching, a dictionary-backed Trie provides $O(L)$ lookup independent of vocabulary cardinality $N$.

Binary Search Invariants & Compact Dict Triepython
import bisect

def count_in_range(sorted_arr: list[int], low: int, high: int) -> int:
    """Count elements x such that low <= x <= high in O(log n)."""
    left = bisect.bisect_left(sorted_arr, low)
    right = bisect.bisect_right(sorted_arr, high)
    return right - left

class PrefixTrie:
    def __init__(self) -> None:
        self.root: dict = {}

    def insert(self, word: str) -> None:
        node = self.root
        for ch in word:
            node = node.setdefault(ch, {})
        node['#'] = True

Common Production & Interview Pitfalls

Frequently Asked Questions

Q: Why is Python dict ordered since Python 3.7?

CPython splits the hash table into a compact `dk_indices` array (storing index positions) and a dense `dk_entries` array (appended in insertion order). This preserves insertion order while reducing memory footprint by ~20-25%.

Q: How do I use a max-heap in Python when heapq only implements a min-heap?

Negate numeric keys on push and pop (`heapq.heappush(h, -val)`), or on Python 3.14+ use the internal max-heap helpers, or wrap custom objects with an inverted `__lt__` comparator.