Reorder List (LC 143): a singly linked list L0 → L1 → … → Ln−1, reordered
in place to L0 → Ln−1 → L1 → Ln−2 → L2 → … by rewiring nodes, never by
moving values. The brief’s sentence is the whole problem — you need the back
half in reverse while still walking the front half forward, and only one of
those directions is free in a singly linked list — so the answer is three
moves in a row, and the piece draws them as pointer writes and nothing else.
The boxes never move; that is where the nodes are in memory. Each next is
an arrow: a straight line to the neighbour, an arc under the row for a
forward skip, an arc over the row the moment a pointer points backward,
which is how you watch the reversed half accumulate. The pointer that changed
on this frame is white. Named variables (slow, fast, prev, cur, a,
b, and the saved nxt / an / bn) sit under the box they hold.
Under the row, walk from head is the list as a traversal would see it at
this exact instant — the nodes reachable from the head, in order, ending in
∅ — with a verdict: well-formed, two lists while the back half hangs
off its own head between the cut and the last weave, or cycle.
The four phases, each a strip label at the top:
- Find the middle.
slowandfastboth start on the head and step 1× and 2× whilefast.nextandfast.next.nextexist, soslowstops on the last node of the front half — the longer half whennis odd. - Cut.
second = slow.next, read before the write, thenslow.next = ∅. The walk from the head is short now, on purpose. - Reverse the back half. The classic
nxt = cur.next; cur.next = prev; prev = cur; cur = nxt, one flip per node, the run arcing backward as it goes. - Weave.
aon the front,bon the reversed back: savea.nextandb.next, writea.next = b, writeb.next = an, advance both; stop whenbruns out. Whatever front node is left over is already in place.
Two controls, one per classic failure.
Cut: forgotten is the one with no error. The reversal does not care that
the front half’s last node still points into it; the weave does not care
either; the final b.next = an reads an from a front node that still
points backward and closes a cycle. The result reads in the right order and
then repeats forever. The walk line calls it before the code could.
Middle: fast = head.next is the habit carried over from cycle
detection. On every even length it is indistinguishable from the correct
loop; on odd lengths it leaves the back half one node longer than the front,
and the weave runs off the end of the front half — an = a.next on ∅ —
one write after everything looked fine. On a one-node list it dereferences
nil before the first step.
Type your own list (up to twelve values), step through one write at a time, or let it run.
Reuse
src/weave.js is a framework-free ES module:
parseList(text, { max, lo, hi })— integers out of free text, capped.expected(values)— the reordered sequence, computed the boring way.reorderList(values)— the plain answer over real nodes, as you would write it in an interview.simulate(values, { split, cut })— runs one configuration and returnsnodes,frames(one per event, each with a full snapshot of everynextpointer and of the named variables), theexpectedand actualresult, the split (mid,front,backHead), andok/crashed/cycle.splitis'front'or'back';cutis'sever'or'forget'. Same trace-per-tick shape asrelink,prune,ledgerandwindow.walk(next, head)— follow the pointers:order,cycle,cycleTo,unreachable,intact.
No rendering or timers in the module; the canvas demo is reference code.
Gotchas
- Both bugs at once on an odd length cancel out — the shorter front half
runs out exactly as the uncut pointer supplies the node the weave was
missing, and the walk reads well-formed and correct. Two wrongs that make a
right on odd
nand a cycle on evennare a good thing to have seen once. slowstays labelled for the whole run: under cut: forgotten it is the node whose pointer never got written, which is where to look when the cycle closes.- The demo bundles its own copy of
weave.js(self-contained by contract); re-copy after editingsrc/.