skip to content

questions

3

Why is index access O(1) in an array but O(n) in a linked list?

level: juniorimportance: must knowfreq 88%

answer

  1. Ask what the machine must compute first
  2. Contiguous equal-sized slots versus scattered nodes
  3. One structure can do address arithmetic
  4. A node knows only its neighbour
  5. Reaching index i means i hops

basics

~20 s

An array stores equal-sized elements in one contiguous block, so the address of element i is one multiply-and-add away. A linked list scatters nodes and joins them by references, so reaching element i means following i links.

solid answer

~50 s

An array is a single contiguous block of equal-sized slots, so the machine reaches element `i` by arithmetic: `base + i * elementSize`. That is a fixed amount of work no matter whether `i` is 3 or 3,000,000 — O(1). A linked list has no such geometry: each node sits wherever memory was available and stores only a reference to its successor, so nothing in the structure encodes "where is number i". The only way to answer is to start at the head and hop i times, which is O(n). Worth adding: the O(n) undersells it, because each hop is a load that depends on the previous load's result, so the machine cannot fetch ahead and each hop can stall on memory. What the list buys in exchange is that it never needs one contiguous block, and that splicing at a node you already hold costs a couple of pointer writes.

code

pseudocode · 10 lines
pseudocode
// contiguous array: constant work, independent of i
value = memory[base + i * elementSize]

// linked list: i dependent hops from the head
node = head
step = 0
while step < i and node != null
    node = node.next
    step = step + 1
value = node.value

go deeper

for a junior

Be ready to answer in one breath: the array computes an element's address, the linked list walks to it, so O(1) versus O(n). Naming contiguity and equal-sized elements as the reason is what separates a real answer from a memorised one.

for a middle

Explain the layout that makes the arithmetic possible, and why each link hop is a load that depends on the previous one, so the machine cannot fetch ahead. Mention that a doubly linked list only halves the walk.

for a senior

Show that you check the access pattern before choosing a structure: any access by position rules the list out immediately, and even a pure front-to-back scan measures much better on the contiguous layout. State the list's genuine compensations rather than dismissing it.

for a principal

Own the framing that a container choice is a commitment to a workload. If a later feature needs positional access or a slice by range, a linked layout either blocks it or forces an index to be built and maintained alongside; say what evidence would justify accepting that.

## The question behind the question "Index access" means: given a position `i`, produce element `i`. Interviewers ask this first because the answer exposes whether you think about *layout* — how the data physically sits in memory — or only about the names of operations. ## Why the array is O(1) An array is defined by two properties that must both hold: 1. every element occupies the **same number of bytes**, and 2. the elements are **contiguous** — element `i+1` begins exactly where element `i` ends. Together those give a closed-form address: ``` address(i) = base + i * elementSize ``` One multiply, one add, one load. The expression contains no loop and no dependence on how many elements exist, so the cost is the same for the first element and the millionth. That is what O(1) means here: constant *with respect to n*, not "free". Note what the array does **not** store: there is no per-element bookkeeping, no forward reference, no index table. The position information is implicit in the geometry. This is also why an array cannot grow in place indefinitely — the contiguity that buys O(1) access is exactly what a resize must reconstruct elsewhere. ## Why the linked list is O(n) A linked list is a set of independently allocated nodes. Each node holds a value and a reference to the next node; the list itself holds only a reference to the head. The nodes may be anywhere, in any order, arbitrarily far apart. Nothing in that layout encodes distance. There is no arithmetic that turns `i` into an address, because the addresses have no pattern. The only available procedure is: ``` node = head for step in 0..i-1 node = node.next return node.value ``` That is `i` hops, so O(i), and O(n) in the worst case. A doubly linked list, which stores a backward reference too, lets you start from the nearer end and so halves the worst case — but halving a linear cost leaves it linear. It is a constant-factor improvement, not a complexity change. ## Why the gap is wider than the notation suggests The asymptotic answer (O(1) versus O(n)) is only half of what a strong candidate says. The other half is that each link hop is a **dependent** load: the machine cannot begin fetching node `k+1` until node `k` has actually arrived, because node `k` contains the address. Hardware is very good at hiding memory latency when it can predict what is needed next — a sequential scan over a contiguous block is the easiest prediction there is — and very bad at hiding it when each address is only revealed at the last moment. So the walk pays close to full memory latency per hop, while the array's arithmetic pays essentially nothing. The consequence is the one that surprises people: even for operations where the two structures share the same complexity class — a full front-to-back scan is O(n) for both — the contiguous structure routinely measures several times to an order of magnitude faster on the same data. Big-O is an upper bound on growth; it deliberately says nothing about constants, and here the constants are the whole story. ## What the list gets in return Be fair to the list, or the answer sounds rehearsed: - it needs no single contiguous block, so it can grow when memory is fragmented; - inserting or removing next to a node you **already hold** is a fixed number of reference writes, with no elements moved; - references to existing nodes stay valid as the list changes around them. None of those requires positional access, which is the point: the list is a structure for *positions you are already standing at*, not for positions you have to name by number. ## Traps - "A doubly linked list gives random access." It does not; it gives a second direction to walk. - "Index access on a list is O(n), but n is small so it is fine." Sometimes true — but say it as a measured claim about your sizes, not as a reflex. - "Arrays are O(1) for everything." Only for access by index. Inserting or deleting in the middle of an array shifts the tail, which is O(n). A useful one-line summary: an array knows *where* everything is; a linked list only knows *what comes next*.

  • Does making the list doubly linked improve index access?
    No. Storing a backward reference lets you start from whichever end is nearer, so the worst case drops from n hops to about n/2. That is a constant factor; the cost is still linear in the index. Complexity classes ignore the factor of two, and the per-hop stall on memory is unchanged.
  • If a program only ever scans from front to back, are the two structures equivalent?
    Asymptotically yes — a full scan is O(n) either way. In measured time, no. The contiguous scan reads addresses the hardware can predict and fetch ahead; the list walk cannot begin the next fetch until the current node arrives. Same complexity class, routinely a large wall-clock gap on the same data.
  • Can a linked list be indexed in O(1) by keeping a side table of node references?
    You can, but you have then built an array of references alongside the list and must keep it in sync on every insert and delete, which is O(n) work per structural change unless the changes are only at the ends. It usually means the list was the wrong choice.

House numbers on one straight street let you drive directly to number 400. A scavenger hunt hands you one clue at a time, so reaching the 400th clue means visiting the 399 before it.

saying these in an interview costs you the question

  • Claims a linked list can jump straight to an index
  • Says doubly linked lists provide random access
  • Thinks index arithmetic gets slower as the array grows
  • Treats the O(n) walk as cheap because references are small
  • Says arrays are O(1) for every operation, including middle inserts

context

open as a page

Under what condition is inserting into a linked list truly O(1)?

level: middleimportance: must knowfreq 76%

basics

~20 s

Only when you already hold a reference to the node you are splicing next to. Then it is a fixed number of reference writes. If you must first reach the position by index or by searching, that walk is O(n) and dominates the cost.

open as a page

A diff swaps a trade blotter's backing array for a linked list to make middle inserts O(1). What is your review?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Reject it as written. The O(1) splice applies only at a node already held; a blotter inserting at a found position still walks O(n), and that walk over scattered nodes measures far worse than the array's contiguous shift of the same length.

open as a page