skip to content

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

level: middleimportance: must knowfreq 60%

answer

  1. what physically moves during a list merge
  2. nodes stay put, links change
  3. an array write can clobber an unread value
  4. count the variables, not the elements
  5. the inputs are consumed, not preserved

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.

solid answer

~40 s

In a list, order is carried by the links, so producing a merged order means rewriting links — payloads never move. The routine holds a cursor into each input plus a tail reference: a fixed count whether the logs hold ten events or ten million, hence O(1) auxiliary space. A one-pass array merge has no such freedom: the winner rarely belongs where it would be written, so writing in place clobbers a value not yet read, and you write into a destination of size n+m. Two caveats. The bound covers the merge, not the structure — every node already pays for its link field, so linked storage costs more total memory for the same events. And the merge is destructive: preserving the original logs means cloning first, which costs O(n).

go deeper

for a junior

Know that merging two sorted linked lists rearranges links between existing nodes rather than building new ones, and that this is why no output buffer is needed.

for a middle

Explain why the array version needs a destination — writing the winner in place would overwrite an element not yet read — and state the list merge's bound as O(n+m) time, O(1) auxiliary space.

for a senior

Add the caveats that matter in production: the merge consumes its inputs, preserving them costs an O(n) clone, and pointer chasing can make the constant-space merge slower in wall-clock terms than copying into contiguous storage.

for a principal

Own the representation decision. Argue when a workload's splice-heavy access pattern justifies linked storage's permanent per-node overhead, and when the team is better served by contiguous buffers plus an occasional O(n) merge buffer.

## What "extra space" means here Auxiliary (extra) space is what an algorithm allocates *beyond* its input and its output. Counting it correctly is the whole question, and it is where the two structures diverge. Take two per-server event logs, each already ordered by timestamp, and merge them into one timeline. ## Why the list merge is O(1) In a linked structure, the ordering *is* the set of links. Each node stores an event and a reference to whoever comes next. To produce a merged order you do not have to move a single event payload — you rewrite the links so that walking from the new head visits the events in timestamp order. The machinery needed for that is fixed: a cursor into each input, a tail reference for the result, and (usually) one sentinel node. Three or four references, regardless of whether each log holds ten entries or ten million. No allocation happens per element, so auxiliary space is O(1) and the merge does zero copying of payloads. This is also why the standard advice "merging sorted sequences needs a scratch buffer" is an array habit, not a law. On a list it is simply false. ## Why the array merge is not A contiguous array stores order positionally: the element at index i comes before the element at index i+1, and there is no link to rewrite. To place the earliest event first in the output you must *write* it to output position 0. If the output is one of the inputs, that write destroys whatever lived there. Concretely: the first input's earliest event is often not the global earliest, so the very first write of an in-place attempt overwrites a value that has not yet been compared, and it is gone — arrays give you no second copy of it. So the one-pass merge writes into a destination of size n+m and reads from both inputs, which is O(n+m) auxiliary space. In-place merge algorithms for arrays do exist; they work by rotating or block-swapping runs rather than by streaming, and they buy their constant space with substantially more complexity and a worse constant factor (some variants pay an extra logarithmic factor in time). They are real, and they are not what anyone writes by default — which is the point: on arrays constant-space merging is an exotic technique, and on lists it is the *only* thing that happens. ## Two directions people get wrong **"O(1) extra space means lists use less memory."** No. It means the *merge* allocates nothing proportional to n. The list already spent one reference per element to exist at all, so for the same events a linked structure occupies more total memory than two arrays did. The merge is cheap in space; the structure is not. **"O(1) extra space means the inputs survive."** No. The merge is destructive by construction: the nodes it relinks are the input nodes. After it returns, walking from an old input head no longer yields that log — it yields a suffix of the merged timeline, interleaved with events from the other server. That is often exactly what you want when combining streams. When it is not, you must clone the nodes first, and that clone is O(n) time and O(n) space — the cost you thought you had avoided has just moved. ## Where the array version is nevertheless the right call The asymptotic score favors the list, and real hardware often does not. A merge over arrays reads two contiguous runs and writes one, a pattern prefetchers handle almost perfectly. A merge over lists chases references that may sit anywhere in memory; every step risks a cache miss, and the miss dominates the comparison by orders of magnitude. For a large one-off merge of bulk log data, copying into contiguous storage, merging there, and rebuilding can beat the elegant constant-space relink, and paying O(n) extra space to do it is a defensible engineering choice. The constant-space list merge wins clearly when the data is *already* in linked form for other reasons — nodes are shared with other structures, elements are spliced and unspliced constantly, or references to individual nodes are held elsewhere and must stay valid. Those are the conditions that made it a list in the first place, and they are the ones to name when you defend the choice. ## Saying it precisely "Merging two sorted lists is O(n+m) time and O(1) auxiliary space, destructive to both inputs; merging two sorted arrays in one pass is O(n+m) time and O(n+m) auxiliary space unless you use a specialized in-place merge." Every clause in that sentence is load-bearing, and dropping the word *auxiliary* or the word *destructive* is what turns a correct answer into a misleading one.

  • After a constant-space merge, can the caller still walk the original first log?
    No. The nodes were relinked into the result, so walking from the old head now yields a suffix of the merged timeline with the other server's events interleaved. If both the originals and the merge are needed, clone the nodes first — which costs O(n) time and O(n) space, putting the price back.
  • Is it fair to say a linked structure uses less memory than arrays because its merge is O(1) extra space?
    No, and it inverts the truth. The O(1) figure is auxiliary space for the merge only. Each node permanently carries at least one reference beyond its payload, plus per-allocation overhead, so the same events cost more memory in linked form than in two arrays. Cheap merging is bought with expensive representation.
  • Given the asymptotics favor lists, when would you still merge in contiguous storage?
    When throughput matters more than the space bound. Contiguous merging reads and writes sequentially, which prefetchers handle well, while relinking chases references and pays cache misses that dwarf the comparisons. For a large batch merge, copying in, merging, and rebuilding is often measurably faster despite the O(n) buffer.

saying these in an interview costs you the question

  • Says merging sorted lists requires allocating a new list of nodes
  • Reads O(1) extra space as a promise the inputs survive
  • Assumes a one-pass array merge can safely write in place
  • Claims linked storage uses less memory because its merge is cheap
  • Denies in-place array merge algorithms exist at all

context