Algorithm

Reverse Nodes in k-Group

Linked List Pattern

Reverse Nodes in k-Group

Given the head of a linked list, reverse its nodes k at a time and return the modified head. Nodes are reversed in consecutive blocks of exactly k; if the final block has fewer than k nodes, it is left in its original order. Rewire nodes in place — do not change any node's value. Return the head of the fully rewired list.

CONSTRAINTS
  • 1 <= k <= n <= 5000
  • 0 <= Node.val <= 1000
  • O(1) extra memory is required (no recursion stack).
EXAMPLE 1
Input: head = [1,2,3,4,5], k = 2
Output: [2,1,4,3,5]
The blocks (1,2) and (3,4) each reverse to (2,1) and (4,3). Node 5 is a block of size 1 — shorter than k — so it stays in place.
EXAMPLE 2
Input: head = [1,2,3,4,5], k = 3
Output: [3,2,1,4,5]
The first three nodes reverse to 3,2,1. The remaining (4,5) is shorter than k = 3, so it is left in its original order.
EXAMPLE 3
Input: head = [1,2], k = 1
Output: [1,2]
Reversing in blocks of size 1 reverses nothing — each block is a single node — so the list is unchanged.
What if the total node count isn't a multiple of k?
The leftover nodes at the end — fewer than k of them — stay in their original order. Only complete blocks of exactly k get reversed.
Can I swap values instead of rewiring pointers?
The intended solution rewires pointers without touching values; some interviewers explicitly forbid value swaps because the point is to master block-wise pointer surgery.
Is a recursive solution allowed?
It's correct and cleaner to read, but it uses O(N/k) stack space. When the problem requires O(1) extra memory, you must reverse each block iteratively.
How is this different from Swap Nodes in Pairs?
It isn't, structurally — Swap Nodes in Pairs is this problem with k fixed at 2. The general k version just needs the scout-ahead check and the group welding written for arbitrary block size.

This is Reverse Linked List with a boundary. There we flipped the entire chain; here we flip it in consecutive blocks of exactly k — [1,2,3,4,5] with k = 2 becomes [2,1,4,3,5]. Two new demands come with the boundary, and they are the whole content of this problem: a partial final block must be left untouched, and each reversed block must be stitched back to its neighbors so the list stays connected.

Recall the base move, then bound it

From Reverse Linked List we already own the reversal engine — the prev/curr/next_temp trio doing cache-flip-advance. The skeleton there had two open slots: where reversal starts and when it stops. For the whole list, it stopped at null. Here we fill the "stops" slot differently: run that same loop exactly k times instead of until null, and it reverses precisely one block, leaving curr parked on the first node of the next block. Nothing about the flip changes; we only change how many times it fires.

The rule that comes first: don't flip a short block

The one hard rule — a final block shorter than k stays in original order — forces a decision before any flipping: we must know a full k nodes exist ahead before we touch them. So each round begins by scouting k nodes forward. If we fall off the end during the scout, this block is short: we return it as-is and stop. Only after confirming k nodes do we reverse. Checking after reversing would be too late — we'd have already scrambled a block we were supposed to leave alone.

The mechanism: reverse a block, then weld

Reversing a block flips who is first and who is last: the block's original head becomes its tail, its original last node becomes its head. Welding means two joins per block — the previous block's tail must point to this block's new head, and this block's new tail (its original head) must point to whatever the next round produces. A dummy node before the real head gives the first block a "previous tail" to attach to, so the head-of-list join is no special case. We track group_prev = the tail of the last finished block.

python
dummy = Node(0, head)
group_prev = dummy

while True:
    # 1. Scout: is there a full group of k ahead of group_prev?
    kth = group_prev
    for _ in range(k):
        kth = kth.next
        if not kth:
            return dummy.next        # short block: leave it, we're done
    group_next = kth.next            # first node AFTER this block

    # 2. Reverse this block (the base engine, stopped at group_next)
    prev, curr = group_next, group_prev.next
    while curr != group_next:
        tmp = curr.next
        curr.next = prev
        prev = curr
        curr = tmp

    # 3. Weld. group_prev.next was this block's OLD head = its new tail.
    old_head = group_prev.next
    group_prev.next = kth            # previous tail -> this block's new head
    group_prev = old_head            # advance to this block's new tail
Crucial Notethe scout in step 1 is what protects the short final block — reverse first and you have already corrupted nodes the rules said to preserve. And in step 2 the reversal is seeded with prev = group_next (not None): that single choice makes the block's new tail point straight at the untouched next block for free, so we never leave a dangling end mid-list. The remaining join — previous tail to this block's new head — is the one explicit rewire, group_prev.next = kth. Miss either weld and the list either breaks in half or points into an already-reversed region.
Worked Example:[1, 2, 3, 4, 5], k = 2
- Scout from dummy: nodes 1, 2 exist — full block. group_next = 3. Reverse (1,2) → 2 → 1. Weld: dummy → 2, and 1 (new tail) already points to 3. List: 2 → 1 → 3 → 4 → 5. Advance group_prev to 1.
- Scout from 1: nodes 3, 4 exist — full block. group_next = 5. Reverse (3,4) → 4 → 3. Weld: 1 → 4, and 3 already points to 5. List: 2 → 1 → 4 → 3 → 5. Advance group_prev to 3.
- Scout from 3: only node 5 remains before null — the scout falls off during k steps. Short block: return dummy.next. Node 5 is left in place.
- Result: 2 → 1 → 4 → 3 → 5.
The cost, and why iterative

Every node is scouted once and flipped at most once, so the work is O(N) time. The constraint demands O(1) space, which is why we write it iteratively: the tidy recursive version (reverse a block, then recurse on the rest) is correct but stacks up to N/k frames — that hidden stack is O(N) space and violates the requirement. The lesson generalizes past this problem: a known sub-routine (here, reversal) becomes a new problem when you add a boundary — where it starts, where it stops, how the pieces re-connect. Solve those three joins and you have reused the engine, not rebuilt it. Swap Nodes in Pairs is this exact problem frozen at k = 2.

Interactive Strategy Visualization

Block-by-Block Reversal

Strategy: k-Length Lookahead
12345
Current Actioncounting nodes...
1. Scout for k Nodes
Before we flip anything, we check if there are at least k (2) nodes available in this chunk.
O(N) Time · O(N) Space Recursive
O(N) Time · O(1) Space Iterative Block Reversal