Question presented to candidate: "Appending to a dynamic array sometimes has to copy every element into a bigger block, which is O(n). Yet everyone says append is O(1). Explain how both can be true, what amortized analysis is, and show it for the Python structures you would use."
What a strong answer should cover:
- Amortized cost = total cost of a worst-case sequence of n operations, divided by n; it is a guarantee, not an average over random inputs.
- The doubling argument: copies 1 + 2 + 4 + β¦ < 2n, so n appends cost under 3n β O(1) amortized, while a single append is still O(n) in the worst case.
- Why the growth must be geometric: growing by a constant amount gives O(nΒ²) total; shrinking must leave a gap (shrink at ΒΌ full, not Β½) or it thrashes.
- The three methods by name (aggregate, accounting/banker, potential) and another example such as a queue built from two stacks.
- Python specifics:
list.appendis amortized O(1) whilelist.pop(0)is O(n) every call; CPython over-allocates by roughly an eighth, which is still a geometric growth factor.
Clarifying questions expected:
- "Do we care about the worst case of a single operation (latency), or only total throughput?"
- "Do we know the final size in advance, so we could pre-allocate?"
Code / implementation expected: Optional β a Python dynamic array or a two-stack queue that counts copies, with the amortized argument stated.