Reversing a linked-list segment between positions m and n: which boundary links must you rewire?
answer
- two seams, not one
- which node keeps its direction?
- the old first node becomes the segment tail
- what does the reference before m still name?
- read the link before overwriting it
basics
~10 sTwo 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.
solid answer
~50 sWalk to the node just before position m, keeping a dummy node in front of the head so that node always exists even when m is 1. From there run the ordinary three-pointer reversal for exactly n − m + 1 steps. When it stops, `prev` names the old n-th node (the segment's new head), `curr` names the node after position n, and `before.next` still names the old m-th node, which has become the segment's tail. Stitch in that order: `before.next.next = curr` first, then `before.next = prev`. Reversing that order destroys the reference you still need and truncates the feed, because the old m-th node's link was set to null on the loop's first step. The whole operation is O(n) time in the worst case, dominated by the walk to position m, and O(1) auxiliary space.
code
pseudocode · 14 linesdummy.next = head
before = dummy
for i in 1..m-1:
before = before.next // node just before position m
prev = null
curr = before.next
for i in m..n:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
before.next.next = curr // old m-th node, now the segment tail
before.next = prev // old n-th node, now the segment head
return dummy.nextgo deeper
Focus on naming the four nodes involved before writing anything: the one before the segment, the segment's first and last, and the one after. Know that a dummy node in front of the head avoids a special case.
An interviewer expects you to run the standard three-pointer loop for exactly the segment length, then explain what each reference names when it stops and why the two stitch assignments must happen in that order.
Show that you check the degenerate cases deliberately — a one-node segment, a segment at the head, a segment at the tail — and give the honest cost, O(n) worst case because reaching the start requires a walk, not O(k).
Own the review standard: in-place relinking with several live references is where correctness bugs hide, so demand that the invariant and the seam order are stated in the code or the review, and that degenerate positions are covered by tests rather than by inspection.
## The shape of the problem A moderation tool has to un-reverse a corrupted stretch of a notification feed: everything outside positions m through n must stay exactly as it is, and the records inside must come back in the opposite order. The interior is the reversal you already know. What makes this a different question is the two seams, and the fact that you must still be holding the right four nodes when the interior loop stops. ## The four references - **before** — the node at position m − 1, the last node that keeps its link direction. After the operation it must point at the segment's new head. - **old m-th** — the segment's first node. After reversal it is the segment's *tail*, and it must point at whatever followed position n. - **old n-th** — the segment's last node. After reversal it is the segment's *head*. - **after n** — the first node outside the segment on the right. It is untouched, but you need its identity to reattach the tail. Only two links are actually written at the seams: `before → old n-th` and `old m-th → after n`. The other two references exist so you can name those two endpoints when the moment comes. ## The dummy node earns its place here When m is 1, there is no node before the segment, and the head of the whole list changes. Without a guard, that becomes a special case with its own branch — and special cases in pointer code are where bugs live. Placing a dummy node in front of the head makes `before` always exist: for m = 1 it is the dummy itself, and the new head is simply read off `dummy.next` at the end. One extra node removes an entire branch, and it is the reason to return `dummy.next` rather than the original head reference, which is stale whenever the segment includes position 1. ## Running the interior loop the right number of times After the walk, set `prev = null` and `curr = before.next`, then run the standard body exactly n − m + 1 times — the number of nodes in the segment, inclusive of both ends. Off-by-one here is the classic failure: n − m steps leaves the last node unreversed and the stitch attaches the wrong node; n − m + 2 steps drags one node in from outside the segment. One detail people miss: on the loop's very first step, the old m-th node's link is set to `prev`, which is null. So from that moment the segment's tail points at nothing, and the right-hand part of the list is held **only** by `curr` as the loop advances. Lose that and the feed ends at position n. ## The stitch, and why its order is not free When the loop stops, `prev` is the old n-th node and `curr` is the node after n. Crucially, `before.next` has not been touched by the loop at all — it still names the old m-th node, which is now the segment's tail. That gives: ``` before.next.next = curr // tail of segment reattaches to the right side before.next = prev // left side attaches to segment's new head ``` Do it in the other order and `before.next` now names the old *n*-th node, so `before.next.next = curr` overwrites a link inside the reversed segment and points the segment's head straight past its own body. The old m-th node still points at null, so the list ends at the wrong place and the remaining records are dropped. The general discipline is the same one that governs whole-list reversal: read every reference you need out of a link *before* you overwrite that link. ## Edge cases worth stating before you write - **m == n** — a one-node segment. The loop runs once, `prev` is that node and `curr` its successor, and the stitch puts everything back exactly as it was. It works without a special case, which is a good sanity check on your loop bounds. - **m == 1** — handled by the dummy, as above; remember to return `dummy.next`. - **n == length** — `curr` ends as null and the segment's tail correctly points at nothing. Again no special case, provided the loop condition counts steps rather than testing for null. - **Invalid input** — m > n, or n past the end. Decide up front whether you validate or assume; saying "I'd assume 1 ≤ m ≤ n ≤ length and validate at the boundary" is a complete answer. ## Cost The walk to position m is O(m) and the reversal is O(n − m + 1), so the total is O(n) in the worst case — the whole list, when the segment sits at the end. A common wrong answer is O(k) for a segment of length k: the segment work is O(k), but you cannot reach position m without walking there, because a singly linked list offers no random access. Auxiliary space is O(1): a fixed handful of references plus the dummy node, none of which grows with the list. Everything is done in one pass over the prefix and one pass over the segment, so no second traversal is needed.
- What changes when the segment starts at position 1?The head of the whole list changes, and there is no real node before the segment. A dummy node placed in front of the head removes both problems: it plays the role of the preceding node, the stitch code stays identical, and the new head is read from the dummy's link at the end. Without it you need a separate branch.
- What is the time and space cost for a segment of length k inside a list of n nodes?O(n) time in the worst case and O(1) auxiliary space. The reversal itself is O(k), but reaching position m costs O(m) because a singly linked list has no random access, and the segment can sit at the far end. Answering O(k) is the standard mistake — it prices the work and forgets the walk.
- How do you convince yourself the interior loop ran the right number of times?Count nodes, not steps: the segment holds n − m + 1 nodes, so the body runs that many times. Check it against m == n, which must run exactly once and leave the list unchanged after stitching. If that degenerate case comes out right, the bounds are almost certainly right.
saying these in an interview costs you the question
- Forgets that no node precedes the segment when m is 1
- Performs the two stitch assignments in the wrong order and truncates the list
- Rebuilds the segment into a new list instead of relinking in place
- Prices the operation as O(k) and ignores the walk to position m
- Runs the interior loop n minus m times, leaving one node unreversed