Reverse Nodes in k-Group
Table of Contents + β
Reverse Nodes in k-Group is a pointer puzzle. Reversing a whole list is easy. But reversing it in fixed blocks, and joining the blocks back correctly, is where people slip. The interviewer wants to see careful pointer work without losing any node.
π― The Problem
You get a linked list and a number k. You reverse the nodes in fixed groups of k.
A linked list is a chain of nodes. Each node holds a value and a pointer to the next node.
The rules:
- Reverse the first
knodes. Then reverse the nextknodes. Keep going. - A group must have a full
knodes to be reversed. - If fewer than
knodes are left at the end, leave that last short group as it is. - Do not change any node values. Change only the links.
Input: 1 -> 2 -> 3 -> 4 -> 5, k = 2Output: 2 -> 1 -> 4 -> 3 -> 5
Explanation: group [1,2] reversed -> 2 -> 1 group [3,4] reversed -> 4 -> 3 node 5 is alone, so it staysSo with k = 2, pairs flip. The leftover node 5 has no partner, so it stays in place.
Here is the list split into groups of two before any reversal.
π’ Approach 1: Copy Values Into an Array (Brute Force)
The idea:
- Copy every node value into an array.
- Reverse each block of
kvalues inside the array. - Write the values back into the list in the new order.
How it works:
- The array holds the values, so reversing a block is just a swap of array slots.
- A short final block stays untouched, the same as the rule says.
Why it is weak:
- It uses an extra array of size n, where n is the node count.
- That is O(n) extra space.
- The interviewer wants the links rewired in place, not the values copied.
Here is the value-array code:
def reverse_k_group(head, k): nodes = [] cur = head while cur: nodes.append(cur.val) cur = cur.next
for i in range(0, len(nodes), k): if i + k <= len(nodes): nodes[i:i + k] = reversed(nodes[i:i + k])
cur = head for value in nodes: cur.val = value cur = cur.next return headβ‘ Approach 2: Count Then Reverse Each Block In Place (Best)
The idea in one line: walk block by block, and for each full block flip the next pointers, then stitch the block back into the list.
The setup:
- Use a dummy head node that points at the real head.
- The dummy gives the first block something to attach to, so the first block is handled like every other block.
- Keep a
groupPrevpointer, the node just before the block you are about to reverse.
How one block works:
- From
groupPrev, walkksteps to findkth, the last node of this block. - If you run off the end, fewer than
kremain. Stop and leave them. - Save
groupNext, the node right after the block. The reversal stops there. - Flip the
nextpointers of theknodes so they point backward. - Attach:
groupPrev.nextbecomeskth, the new block head. - Move
groupPrevto the old first node, which is now the block tail.
Why it is fast:
- Each node is touched a constant number of times.
- That is O(n) time.
- It rewires existing nodes, so it uses only a few pointers. That is O(1) extra space.
This is the rewiring for the first block, where k = 2 and nodes 1 and 2 flip.
Steps to Solve
- Make a dummy node and point it at the head. Set
groupPrevto the dummy. - From
groupPrev, walkksteps to findkth, the last node of this block. If you run off the end, stop. - Remember
groupNext, the node right afterkth. - Reverse the block of
knodes so they point backward, ending atgroupNext. - Reconnect:
groupPrev.nextbecomes the old block tailβs value, and movegroupPrevto the old first node. - Repeat for the next block. Return
dummy.next.
This Python version reverses each block in place using a dummy head and the groupPrev pointer to join blocks.
class ListNode: def __init__(self, val): self.val = val self.next = None
def get_kth(cur, k): # walk k steps from cur, or None while cur and k > 0: cur = cur.next k -= 1 return cur
def reverse_k_group(head, k): dummy = ListNode(0) # fake node before the list dummy.next = head group_prev = dummy
while True: kth = get_kth(group_prev, k) # last node of this block if not kth: # fewer than k nodes left break group_next = kth.next # node right after the block
prev = group_next # reverse the block cur = group_prev.next while cur != group_next: tmp = cur.next cur.next = prev prev = cur cur = tmp
old_first = group_prev.next # becomes the block tail group_prev.next = kth # attach the new head group_prev = old_first # move to next block return dummy.next
def build(values): head = ListNode(values[0]) cur = head for v in values[1:]: cur.next = ListNode(v) cur = cur.next return head
head = build([1, 2, 3, 4, 5])head = reverse_k_group(head, 2)
out = []while head: out.append(str(head.val)) head = head.nextprint(" -> ".join(out))The output of the above code will be:
2 -> 1 -> 4 -> 3 -> 5Let us trace the Python version line by line, because the pointer joins are the part people get wrong.
get_kth(cur, k) walks k steps forward from a node. It returns the kth node ahead, or None if the list ends first. We use it to test whether a full block of k nodes really exists.
In reverse_k_group, we make a dummy node and point it at the head. The dummy gives the first block something to attach to, so the first block is handled the same way as every other block. group_prev starts at the dummy. group_prev is always the node sitting just before the block we are about to reverse.
The loop runs forever until we break. First we call get_kth(group_prev, k) to find kth, the last node of this block. If it is None, fewer than k nodes remain, so we stop and leave them. We save group_next, the node right after the block, because the reversal will need it as the stopping point.
Now we reverse the block. We set prev to group_next so the old first node ends up pointing at the node after the block. Then we walk from group_prev.next to group_next, flipping each next pointer to point backward.
After the flip, kth is the new first node of the block. group_prev.next still points at the old first node, which is now the block tail. We save it as old_first. Then we set group_prev.next = kth to attach the reversed block. Finally we move group_prev to old_first, ready for the next block. We return dummy.next, the real head.
β±οΈ Time and Space Complexity
The array copy is simple but uses O(n) extra space. The in-place reversal touches each node a constant number of times, so it runs in O(n) time. It uses only a handful of pointers, so it is O(1) extra space. That O(1) space is the answer the interviewer is looking for.
| Approach | Time Complexity | Space Complexity |
|---|---|---|
| Copy values into an array | O(n) | O(n) |
| In-place block reversal | O(n) | O(1) |
Tip
Always check that a full group of k nodes exists before you reverse it. If you reverse a short leftover group by mistake, your output will not match the problem rule. The leftover stays as is.
π§© Key Takeaways
- β Reverse the list in fixed blocks of k, and leave a final short block untouched.
- β Use a dummy head so the first block joins the same way as every other block.
- β Keep a group_prev pointer just before each block to reconnect the pieces correctly.
- β The in-place version uses only a few pointers, so it is O(1) extra space.
- β Count k nodes ahead before reversing, or you may flip a group that should stay.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
In Reverse Nodes in k-Group, what happens to a final group with fewer than k nodes?
Why: Only full groups of k nodes are reversed. A short leftover group stays in its original order.
- 2
Why do we use a dummy head node?
Why: The dummy gives the first block a node to attach to, removing a special case for the head.
- 3
What does the group_prev pointer track?
Why: group_prev sits right before the block, so it can connect to the new head after reversal.
- 4
What is the space complexity of the in-place block reversal?
Why: It rewires the existing nodes using only a few pointers, so it uses constant extra space.