Indexed Vector as a Linked List with Checkpoints Every k Nodes
Company: OpenAI
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: hard
Interview Round: Onsite
Implement a simplified vector: an ordered, dynamically sized sequence of strings accessed by integer position. "Vector" here means a vector in the sense of C++ `std::vector` or Java `ArrayList`, not a vector database of embeddings. It supports two operations:
- `add(index, val)`: insert the string `val` at position `index`. The element previously at `index` and every element after it move one position to the right.
- `get(index)`: return the string at position `index`.
```text
v.add(0, "a") # ["a"]
v.add(1, "c") # ["a", "c"]
v.add(1, "b") # ["a", "b", "c"]
v.add(0, "z") # ["z", "a", "b", "c"]
v.get(2) # returns "b"
```
The interview moved in two steps: a simple first design was judged not efficient enough, and the interviewer then asked for a specific structure, a linked list with checkpoints.
### Clarifying Questions
- Is `add` allowed at `index == size`, appending at the end? What should happen for an index outside the valid range of `add` or `get`?
- Is the checkpoint interval `k` a fixed parameter, or should the structure choose it and adapt it as the sequence grows?
- Are deletions or in-place updates needed, or only `add` and `get`?
- Which operation dominates the expected workload?
### Part 1 — A plain linked list and its cost
Implement `add` and `get` on a plain singly linked list and state the worst-case cost of each. Explain why this design was judged not efficient enough, and how its costs compare with those of an array-backed sequence.
```hint Count the walk
For each operation, count how many nodes you must visit before the actual work can happen, both for the worst position and for an insertion at the front.
```
#### What This Part Should Cover
- Correct `add` and `get`, including insertion at the front and at the end
- The worst-case cost of each operation, and why reading by position is the bottleneck
- The opposite trade-off of an array-backed sequence
### Part 2 — Linked list with checkpoints
In addition to the list, store a pointer to every k-th node, called a checkpoint. `get(index)` first jumps to the checkpoint at or before `index` and then walks forward, so a read costs O(k) instead of O(n).
Implement `add` and `get` on this structure. The core difficulty is keeping the checkpoints correct after an insertion: an insertion at the front or in the middle changes which node every later checkpoint should point to, and as the sequence grows, new checkpoints may be needed.
```hint Where each checkpoint must go
After an insertion at position i, pick one checkpoint beyond i and work out which node it should now reference, and where that node sits relative to the one it referenced before.
```
```hint Count the checkpoints
Compare how many checkpoints a sequence of length n needs with how many a sequence of length n + 1 needs.
```
#### Clarifying Questions for this Part
- Must checkpoint `j` always point at exactly position `j * k`, or may checkpoints drift as long as `get` stays bounded?
#### What This Part Should Cover
- A precise checkpoint invariant and how `get` uses it
- How insertions at the front, in the middle and at the end update the checkpoints, and what that costs
- When a new checkpoint is created as the sequence grows
- The resulting costs of `add` and `get` in terms of n and k, and how to choose k
### What a Strong Answer Covers
- Clarifying the meaning of "vector" and the index rules before designing anything
- Code that handles the empty sequence, the front, the end and out-of-range indices
- An invariant that the code demonstrably maintains, with tests that check it after many random insertions
- A justified choice of k and the trade-off it controls between `add` and `get`
- Awareness of other structures with different bounds, and when each fits
### Follow-up Questions
- How should k change as the sequence grows from a handful of elements to a very large sequence, and what does re-spacing the checkpoints cost when amortized over the insertions?
- How would you add `remove(index)` while keeping the checkpoint invariant?
- Which structure gives O(log n) for both operations, and when would you still prefer the checkpointed list or a plain array?
Overview: Implement an indexed dynamic sequence of strings, in the sense of an array list, that supports inserting at a position and reading by position. Starting from a plain singly linked list, build a linked list with a checkpoint every k nodes so reads cost O(k), and keep the checkpoints correct as insertions shift positions.
Read the full OpenAI Software Engineer interview experience this question came from