Problem Walkthrough

Reverse Nodes in k-Group LeetCode 25

Count k nodes ahead, reverse each segment in-place, then connect the reversed chunks together — the hard linked list problem that separates good from great.

10 min read|

Reverse k-Group

LeetCode 25

Problem Overview

LeetCode 25, Reverse Nodes in k-Group, gives you the head of a singly linked list and an integer k. Your task is to reverse every k consecutive nodes and return the modified list. If the number of remaining nodes is less than k, those nodes must be left as-is — do not reverse them.

Critically, the problem forbids swapping values. You must change the actual next pointers, making this a genuine pointer manipulation challenge. This constraint rules out the naive trick of collecting values into an array, reversing chunks, and writing them back.

Constraints worth knowing: the number of nodes n satisfies 1 <= n <= 5000, values are in 1..1000, and 1 <= k <= n. When k equals 1, the list is unchanged. When k equals n, the entire list is reversed.

Key constraint bullets for quick reference:

  • Must change links, not values
  • 1 <= k <= n <= 5000
  • Remaining nodes fewer than k stay in original order
  • O(1) extra space expected (no new nodes)

The Strategy

Use a dummy node whose next pointer initially points to head. This dummy becomes the anchor for the first group and eliminates special-casing when the first group's head changes after reversal.

For each group, track groupPrev — the tail of the already-processed portion. From groupPrev.next, count k nodes forward. If fewer than k nodes exist, stop: the remainder stays unchanged. Otherwise, perform an in-place reversal of those k nodes, then reconnect: groupPrev.next points to the new head of the reversed segment, and the new tail points to the start of the next segment.

Advance groupPrev to the new tail (the original group head) and repeat. Each full pass through the list processes every node exactly once, giving O(n) time and O(1) extra space.

💡

Mental Model

Think of the problem as repeated applications of "reverse a linked list" on k-sized chunks, with careful pointer bookkeeping before and after each reversal to keep the chain intact.

Counting k Nodes

Before reversing any segment, verify that at least k nodes exist starting from the current position. Walk k steps forward from groupPrev.next; if you run out of nodes before counting k, the remaining segment is left unchanged and the algorithm terminates.

This check is the guard that makes the "leave remaining nodes as-is" requirement trivially correct. No special code is needed after the loop — once the count fails, simply break out and return dummy.next.

A helper function canAdvance(node, k) that returns the k-th node or null is a clean way to encapsulate this check. It also doubles as the pointer you hand off to the reversal step, so you avoid traversing the segment twice.

  1. 1Set cur = groupPrev.next
  2. 2Walk k steps: if cur becomes null before k steps, break the outer loop
  3. 3Record kthNode = cur (the last node of this group)
  4. 4Proceed to reversal using [groupPrev.next, kthNode]

Reversing a k-Node Segment

Standard iterative linked-list reversal works within the segment. Initialize prev = null (or the node that will follow the group) and cur = groupPrev.next. Iterate k times: save cur.next, point cur.next to prev, advance prev and cur. After k iterations, prev is the new head of the reversed segment.

One subtle but important detail: before reversing, save a reference to groupPrev.next. This node is the head of the current group and, after reversal, becomes the tail of that same group. You will need it immediately after reversal to reconnect groupPrev to the new head and to set the new tail's next pointer.

Setting the reversal boundary correctly is the most common source of bugs. The reversal loop should run exactly k times — not until cur is null, which would reverse the entire remaining list instead of just the group.

ℹ️

Key Insight

The original head of each group becomes its tail after reversal. Save this pointer before you start reversing — you will need it to connect the reversed segment to the next group.

Connecting Segments

After reversing a k-node group, three pointer updates are required. First, groupPrev.next = newGroupHead reconnects the processed portion of the list to the reversed segment. Second, groupTail.next = nextGroupStart attaches the reversed segment to whatever comes after it, whether that is the next group or the unreversed remainder.

Finally, advance groupPrev = groupTail to set up for the next iteration. The loop then repeats: count k nodes from groupPrev.next, reverse if possible, reconnect, advance groupPrev.

A common off-by-one arises when groupTail.next is not set before the reversal reuses that pointer. Always capture nextGroupStart = kthNode.next before reversing, since the reversal will overwrite kthNode.next.

  1. 1Capture nextGroupStart = kthNode.next before reversing
  2. 2Reverse the k-node segment (prev pointer ends at new head)
  3. 3groupPrev.next = newGroupHead (reconnect left side)
  4. 4groupTail.next = nextGroupStart (reconnect right side)
  5. 5groupPrev = groupTail (advance for next iteration)

Code Walkthrough — Python and Java

In Python: create dummy, set groupPrev = dummy. In a while loop, call a helper to get the k-th node from groupPrev.next; if None, break. Capture groupTail = groupPrev.next and nextStart = kthNode.next. Reverse from groupTail k times using prev = nextStart as the sentinel. Then groupPrev.next = prev (new head), groupTail.next = nextStart (already set by reversal), groupPrev = groupTail. Return dummy.next. Time O(n), space O(1).

In Java: same structure — a ListNode dummy, a while loop guarded by a getKth helper returning null when fewer than k nodes remain. The reversal inner loop runs exactly k times. The reconnection lines mirror Python. Both solutions are around 20-25 lines of clean code.

A subtle Java pitfall: initialize prev = nextStart inside the reversal, not null. If you initialize prev = null the last node of the group incorrectly points to null instead of the start of the next group, breaking the list silently.

⚠️

Do Not Skip the Dummy Node

The dummy node is essential. Without it, reversing the first group requires special-case code to update the head reference. With dummy, the first group is treated identically to all subsequent groups — groupPrev.next = newHead just works.

Ready to master algorithm patterns?

YeetCode flashcards help you build pattern recognition through active recall and spaced repetition.

Start practicing now