CodeOath
← All posts
Python70 min total · 18 parts

Python Fundamentals for Interviews: Data Structures, Comprehensions, and Gotchas

Contents — Part 2 of 18: The Four Core Collection Types
Part 2 of 18 · ~3 min

The Four Core Collection Types

TypeOrderedMutableDuplicatesTypical use
listYesYesYesA general-purpose, ordered sequence
tupleYesNoYesA fixed, immutable record — e.g. (x, y) coordinates
setNoYesNoFast membership checks, deduplication
dictInsertion order (3.7+)YesKeys uniqueKey-value lookups

The interview-relevant distinction is list vs. tuple: choosing a tuple isn't just style — it signals "this shouldn't change," and it's what lets a tuple be used as a dict key or set member (since those require hashable, immutable values — a list can't be either).

point = (3, 4)                  # tuple — immutable, hashable
seen_points = {(3, 4), (1, 2)}  # a set of tuples — this works
seen_points = {[3, 4]}          # TypeError: unhashable type: 'list'

Big-O cheat sheet for the built-ins

Knowing why a set or dict lookup is fast (average O(1), backed by a hash table) versus a list's O(n) linear scan is one of the most commonly probed pieces of Python knowledge in interviews:

Operationlistdict / set
x in collectionO(n)O(1) average
Append / addO(1) amortizedO(1) average
Insert at index 0O(n) — shifts everythingN/A
Lookup by key/indexO(1) by index, O(n) by valueO(1) average by key

That single row — x in collection — is the reason "reach for a set when you only need membership testing" shows up in almost every "how would you optimize this" interview follow-up. A list of 10,000 items scanned repeatedly inside a loop turns an O(n) check into an O(n²) algorithm; swapping it for a set fixes the asymptotic complexity with no other code changes.

collections — the standard library's specialized containers

The four built-ins above cover most code, but the collections module is worth knowing for the cases they don't fit well:

from collections import defaultdict, Counter, deque, namedtuple

# defaultdict — no more "if key not in dict: dict[key] = []"
groups = defaultdict(list)
for name, team in [("Ana", "A"), ("Bo", "B"), ("Cy", "A")]:
    groups[name_team := team].append(name)  # groups["A"] auto-creates [] on first access

# Counter — frequency counting in one line
counts = Counter("mississippi")  # Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})
counts.most_common(2)            # [('i', 4), ('s', 4)]

# deque — O(1) appends/pops from BOTH ends, unlike a list (O(n) from the front)
queue = deque([1, 2, 3])
queue.appendleft(0)  # deque([0, 1, 2, 3]) — a plain list.insert(0, x) is O(n)

# namedtuple — a lightweight, immutable record with named fields
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
p.x, p.y  # (3, 4) — reads like an object, behaves like a tuple

deque is the one to remember for interview questions specifically: a plain list used as a queue (list.pop(0)) is O(n) per operation because every remaining element has to shift down one index; a deque makes both ends O(1).