skip to content

Linked Lists

Node-and-pointer lists: how they live in memory, the singly/doubly/circular variants, and the core pointer manipulations interviewers expect you to explain step by step. Linked lists are the classic probe for whether you truly understand references, ownership of the 'next' pointer, and cost models beyond Big-O labels.

part ofData structures & algorithmsoverview, primer and where to startread it →
on this pageshow

explore

questions

page 1 of 2

In a linked list, why is there no address arithmetic to reach the k-th node?

level: juniorimportance: must knowfreq 85%

answer

  1. What must be true to compute an address
  2. Same size, one after another
  3. Nodes are separate allocations
  4. Only the link field says where next is
  5. No base plus stride means walking

basics

~20 s

A linked list's nodes are separately allocated and scattered in memory, so there is no base address plus fixed stride to compute. Each node knows only where the next one lives, so reaching index k means following k links.

solid answer

~50 s

Constant-time indexing needs two guarantees: elements of a known fixed size, laid out contiguously from a known base address. A chain of nodes gives you neither. Each node is its own allocation, handed out wherever the allocator had room, and the only thing holding the structure together is that every node stores a reference to the next one. There is no `base + k * stride` to evaluate — the address of node k is genuinely unknown until you have read the k-1 nodes before it, so positional access is O(k) and O(n) in the worst case. Worse, those are *dependent* loads: hop k cannot start until hop k-1 has returned its address. The compensation is that nodes never move, so a reference you hold into the middle of the chain stays valid for the life of that node.

code

pseudocode · 13 lines
pseudocode
// each node is its own heap record:
//   node.value  - the payload
//   node.next   - reference to the next record, or null at the end

node_at(head, k):
    node = head
    for i in 0..k-1:
        if node == null:
            return null          // ran off the end
        node = node.next         // address known only after this load returns
    return node

// cost: k hops, each one dependent on the previous

go deeper

for a junior

Be ready to say what a node contains — a value and a reference to the next node — and why that forces a walk from the head to reach position k. Naming the two guarantees that constant-time indexing needs is the whole answer.

for a middle

Explain the mechanics: separate allocations mean no base address and no uniform stride, so the address of node k exists only after the previous k reads. Mention that the hops are dependent loads, not just more instructions.

for a senior

Show you have seen the cost in production: an O(n) pass over a chain and an O(n) pass over a contiguous block are not interchangeable, and a loop that re-resolves positions from the head is the quadratic bug that shows up under real data volume.

for a principal

Own the trade being made. Giving up positional arithmetic and locality buys stable addresses and size-independent local edits; be able to say which of those a given workload actually needs before a team commits to the layout.

## Two ingredients make indexing constant time When a container answers "give me element k" in constant time, it is doing arithmetic, not searching. That arithmetic needs exactly two guarantees: 1. **Uniform stride** — every element occupies the same number of bytes, so position k is k strides from the start. 2. **Contiguity** — the elements sit in one unbroken block starting at a known base address. Given both, the address of element k is `base + k * stride`: a multiply, an add, and one load. Notice what is missing from that expression — `n`. The size of the container never enters it, which is exactly why the cost does not grow with the container. ## What a node actually is A linked-list node is a small independent record on the heap. It holds the payload (the value, or a reference to it) and a link field: a reference to the next node, or a null terminator at the end. The owner of the list holds one reference — the head. Every other node is reachable *only* by starting there and following links. The crucial point is where those records live. Each one is a separate allocation request, satisfied wherever the allocator happened to have a suitable free block at that moment. Two nodes created back to back may land beside each other; two nodes created ten minutes apart, with other allocations in between, may land megabytes apart. Nothing in the structure enforces or even prefers an ordering in memory. **The order of a linked list is logical, not physical.** The chain is a story the link fields tell about records whose actual addresses are arbitrary. So neither ingredient holds. Stride is meaningless because nodes are not spaced regularly, and there is no base address for the sequence because the sequence has no block of its own. The arithmetic has nothing to work with. ## Consequence one: positional access is traversal To reach index k you start at the head and follow the link field k times. That is O(k) hops, O(n) for the last element, and O(n) if you scan from the head repeatedly — the classic quadratic accident is a loop that walks positions 0, 1, 2, ... by re-traversing from the head each time, turning an O(n) pass into O(n^2). A traversal must also respect the terminator invariant: after each hop, the reference may be null, and reading a field off it is the standard crash in beginner code. That is why the hop and the null check belong in the same loop body. ## Consequence two: the hops are dependent There is a second, subtler cost that big-O hides. Scanning a contiguous block, the machine knows every address it will need in advance; the reads can be issued and overlapped. Walking a chain, the address of the next node is *inside the node you are currently reading*. The load for hop k cannot even be issued until the load for hop k-1 has come back. If the nodes are scattered, each hop is a memory access whose latency cannot be hidden behind the previous one, so the delays add up rather than overlap. Two structures can share the label O(n) for a full pass and still differ by an order of magnitude in wall-clock time for exactly this reason. Big-O counts operations; it does not price them. ## Consequence three: what the layout buys you The same property that kills indexing is what the structure is for. Because a node is its own allocation, it never moves: adding a million more elements does not relocate the node you are looking at, and a reference you stored to it days ago still points at it. There is no capacity, no growth threshold, and no bulk copy — the structure grows by one allocation at a time. And because neighbours are joined only by link fields, changing the sequence around a node you already hold is a couple of reference writes, not a shift of everything after it. That is the actual trade the node-and-pointer model makes: it gives up positional arithmetic and memory locality, and gets back stable addresses and local, size-independent restructuring. ## Where candidates go wrong The common failure is answering as if a linked list were an array with extra steps — "the runtime knows where node k is", or "it caches positions after the first pass". There is no hidden index; the only map from position to address is the walk itself. The other failure is dismissing O(n) indexing as "a bit slower": at ten million elements, a positional loop written the naive way is not a bit slower, it is a different program.

  • If positional access is O(k), why does anyone iterate a linked list at all?
    Iterating from the head to the tail once is O(n) total, because you keep the current node reference and hop from it. The quadratic trap is asking for position 0, then 1, then 2 from the head each time — that re-walks the prefix and turns an O(n) pass into O(n^2). Iterate with a cursor you carry, never with a position you re-resolve.
  • Why does a full traversal of a chain often run far slower than a contiguous scan of the same element count?
    Both are O(n) passes, but the per-element cost differs. A contiguous scan knows all its addresses up front, so memory reads overlap. In a chain, the address of the next node lives inside the current node, so each read must complete before the next can be issued. Scattered nodes turn that into a serialized chain of memory latencies that big-O never mentions.
  • What does the node-and-pointer layout give you that contiguous storage cannot?
    Address stability. A node never moves as the structure grows, so a reference you took to it stays valid indefinitely, and inserting or removing near it touches only the neighbouring link fields instead of shifting or reallocating everything else. That is what makes a held-node edit cheap regardless of how large the structure is.

Numbered lockers let you walk straight to locker 400 because they are the same size and in a row. A treasure hunt gives you one clue at a time, and the only way to reach the fourth clue is to find the first three.

saying these in an interview costs you the question

  • Says the runtime keeps a hidden index of node positions
  • Assumes nodes are laid out consecutively in memory
  • Claims positions get cached after the first traversal
  • Calls indexing O(1) because 'following a pointer is fast'
  • Re-walks from the head inside a loop and calls it linear

context

open as a page

Why does a dummy head node remove the special case for deleting the first element?

level: juniorimportance: must knowfreq 66%

basics

~20 s

A dummy head is a permanent node placed before the first real element, so every real node has a predecessor. Deletion is always relink-the-predecessor, which means the first element needs no branch of its own.

open as a page

In a singly-linked list, why is appending at the end O(n), and what makes it O(1)?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Appending is O(n) when only a head reference is kept, because the last node is reachable only by walking the whole chain. Storing a tail reference alongside the head makes append O(1) in any of the three variants.

open as a page

How do you detect whether a singly linked list has a cycle, and why does a plain walk hang?

level: juniorimportance: must knowfreq 78%

basics

~20 s

Walk two references, one hopping one node per step and one hopping two. If they meet, the list has a cycle; if the fast one runs off the end, it does not. A plain walk never reaches an end, so it spins forever.

open as a page

Why do sorted-list merge routines start with a dummy head node?

level: juniorimportance: must knowfreq 74%

basics

~20 s

A dummy head gives the merge somewhere to attach the first winner before any result exists, so the loop body never special-cases an empty result or reassigns the head. You return the dummy's successor and throw the sentinel away.

open as a page

How do fast and slow pointers find the middle of a singly linked list in one pass?

level: juniorimportance: must knowfreq 85%

basics

~20 s

Advance two references from the head at different speeds: slow moves one node per step, fast two. When fast runs off the end it has covered twice the distance, so slow is sitting at the middle. One sweep, constant extra memory.

open as a page

In iterative singly-linked-list reversal, why must you save the next node before rewiring?

level: juniorimportance: must knowfreq 88%

basics

~20 s

Rewiring the current node's outgoing link overwrites the only handle on the rest of the list. Saving the successor into a temporary reference first keeps that handle; without it, every node past the current one becomes unreachable.

open as a page

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

level: juniorimportance: must knowfreq 88%

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.

open as a page

Why is splicing a node into a chain O(1) only when you already hold the node?

level: middleimportance: must knowfreq 70%

basics

~20 s

Rewiring is a fixed number of reference writes that touch only the nodes next to the splice point, so it costs the same at ten elements or ten million. Finding that splice point is a separate O(n) walk, and the O(1) claim covers only the rewiring.

open as a page

Why is skip-list search expected O(log n) rather than worst-case, and what degrades it?

level: middleimportance: must knowfreq 62%

basics

~20 s

A skip list picks each key's tower height by repeated coin flips, so its shape is random, not enforced. The O(log n) bound holds in expectation over those flips; if promotions dry up — a broken or degenerate random source — every tower is height 1 and search falls back to an O(n) chain walk.

open as a page

In a doubly-linked list, why is deleting a node you already hold O(1) but O(n) in a singly-linked list?

level: middleimportance: must knowfreq 78%

basics

~20 s

Unlinking a node means rewriting its predecessor's next reference. A doubly-linked node stores prev, so both neighbours are one hop away — O(1). A singly-linked node cannot look backwards, so its predecessor must be found by walking from the head.

open as a page

In tortoise-and-hare cycle detection, how do you locate the cycle's entry node after the collision?

level: middleimportance: must knowfreq 66%

basics

~20 s

Leave one reference on the collision node, move the other back to the head, then advance both a single node per step. They meet exactly on the cycle's first node, because the head-to-entry distance and the collision-to-entry distance differ only by whole laps.

open as a page

Why can a sorted linked-list merge run in O(1) extra space when the array version cannot?

level: middleimportance: must knowfreq 60%

basics

~20 s

A list merge only rewrites link fields on nodes that already exist, so it needs a fixed handful of references. A one-pass array merge must write into a separate destination, since writing in place would overwrite an element not yet read.

open as a page

On an even-length linked list, which of the two middle nodes does a fast/slow walk land on?

level: middleimportance: must knowfreq 62%

basics

~20 s

It depends where fast starts. With fast starting at the head, slow lands on the second of the two middles; with fast one node ahead, on the first. Neither is more correct — pick one and prove it on a two-node chain.

open as a page

In iterative linked-list reversal, what invariant holds for prev and curr at each loop top?

level: middleimportance: must knowfreq 70%

basics

~20 s

At every loop top, prev heads the already-reversed prefix and curr heads the untouched remainder. The two are disjoint and together hold every node, so when curr is null, prev heads the whole reversed list.

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

Why does a skip list search faster than a sorted linked list holding the same keys?

level: juniorimportance: should knowfreq 50%

basics

~20 s

A sorted linked list must be walked one node at a time, so lookup is O(n). A skip list stacks sparse express levels above that list, so a search gallops far, drops down, and skips most nodes — expected O(log n).

open as a page

Why does a linked list of 10 million 8-byte readings dwarf a flat buffer in memory?

level: middleimportance: should knowfreq 45%

basics

~20 s

Each reading becomes its own heap record: the 8-byte value, a link field of one machine word, and the allocator's per-block header and size rounding. That is commonly 24 to 32 bytes per element instead of 8 — a three- to fourfold blow-up, not a saving.

open as a page

In a doubly-linked sentinel ring, why do insert and remove need no null checks?

level: middleimportance: should knowfreq 46%

basics

~20 s

In a sentinel ring every node always has a non-null prev and next, because both ends wrap through the sentinel. Insert and remove become a fixed handful of pointer assignments with no null or end-of-list tests.

open as a page

Why does a skip-list insert cost no more than the search that precedes it?

level: middleimportance: should knowfreq 44%

basics

~20 s

The descending search already visits, on every level, the last node whose key is smaller than the target. Recording those predecessors turns insertion into a few pointer rewires at the new tower's levels — no rebalancing, no traversal repeated, so insert is expected O(log n) dominated by the search.

open as a page

In a circular singly-linked list, why does walking until a NIL next reference never terminate?

level: middleimportance: should knowfreq 52%

basics

~20 s

A circular list has no NIL next reference: the last node points back to the first, so the usual end test never fires. Stop by node identity instead — advance first, then halt on returning to the node you started from.

open as a page

In tortoise-and-hare cycle detection, why can't the fast reference skip past the slow one?

level: middleimportance: should knowfreq 60%

basics

~20 s

Inside the loop the fast reference gains exactly one node per step, so the gap between the two counts down one at a time and must pass through zero. Skipping over would require the gap to change by two or more.

open as a page

Given only a node in a singly linked list and no head, how do you remove it and when does that fail?

level: middleimportance: should knowfreq 58%

basics

~20 s

Copy the successor's payload into the node you hold, then splice the successor out. The list shrinks correctly, but the object unlinked is the successor. It fails on the last node, which has no successor to borrow from.

open as a page

In a single pass over a singly linked list, how do you advance prev and curr while deleting nodes?

level: middleimportance: should knowfreq 52%

basics

~20 s

Advance prev only when a node survives. After unlinking curr, leave prev where it is and move curr forward alone. Advancing prev unconditionally parks it on an already-removed node, so back-to-back deletions leave the second node linked in.

open as a page

What breaks if you split a linked list for merge sort without severing the first half?

level: middleimportance: should knowfreq 46%

basics

~20 s

Nothing is actually split. The first half's last node still links to the second half, so the first recursive call receives the whole list again, the subproblem never shrinks, and the recursion runs until the stack is exhausted.

open as a page

Why does a fast/slow middle loop test both fast and fast.next before every double hop?

level: middleimportance: should knowfreq 50%

basics

~20 s

Each clause prevents a different crash. Testing fast covers even-length chains, where fast has already run past the end; testing fast.next covers odd-length chains, where fast sits on the last node and the second hop has nowhere to go.

open as a page

Reversing a linked-list segment between positions m and n: which boundary links must you rewire?

level: middleimportance: should knowfreq 50%

basics

~10 s

Two boundary links change: the node before position m must point at the old n-th node, and the old m-th node, now the segment's tail, must point at the node after position n.

open as a page

Why does a real-time buffer pool use an intrusive free list instead of a growth-doubling array of free slots?

level: middleimportance: should knowfreq 45%

basics

~20 s

A growth-doubling array is O(1) only amortized: one push can reallocate and copy everything, which a hard deadline cannot absorb. An intrusive free list is worst-case O(1) and stores its links inside the free buffers themselves, so it never allocates at runtime.

open as a page

showing 1–30 of 43