A strong answer
A dynamic array keeps its elements in one block with spare capacity. Appends write into spare slots in O(1). When the block is full, the array allocates a larger block, copies every element over and frees the old one, which costs O(n) for that one append.
The growth rule decides the total cost. With doubling, an array that grows from 1 to n elements resizes at sizes 1, 2, 4, …, so it copies 1 + 2 + 4 + … + n/2 + n elements, which is less than 2n. Spread over n appends, that is fewer than two copies each: O(1) amortized. With a fixed step of k slots, it resizes every k appends and copies k, 2k, 3k, … elements, about n²/(2k) in total, so each append costs O(n) on average. Any constant factor above 1 gives amortized O(1); the factor only trades memory against copying. Doubling copies less but can leave half the block unused; Java's ArrayList uses 1.5 and CPython's list about 1.125 to waste less memory.
Two practical consequences: reserving capacity up front, when the final size is known, avoids every resize; and a resize moves the elements, so in C++ pointers and iterators into a vector are invalidated when it grows.
What the interviewer is listening for
- Size versus capacity, and why a block can't be extended in place.
- The geometric-series argument for doubling and the quadratic total for fixed steps.
- The word "amortized", used correctly: individual appends can still be O(n).
- At least one real growth factor and the memory trade-off.
Common mistakes
- Saying every append is O(1) without qualification.
- Thinking a larger fixed step fixes the problem. It only changes the constant.
- Forgetting that shrinking needs hysteresis, or an array oscillating at the boundary resizes on every operation.