Pattern family: LinkedList Manipulation
Difficulty range: Easy β Hard
Language: Java
- How to identify this pattern β start here
- What is this pattern?
- Core rules
- 2-Question framework
- Variants table
- Universal Java template
- Quick reference cheatsheet
- Common mistakes
- Complexity summary
Scan the problem statement for these phrases first.
| If you see this... | It means |
|---|---|
| "reverse a linked list" | full reversal |
| "reverse between positions p and q" | partial reversal with skip |
| "reverse in pairs" | k=2 chunked reversal |
| "reverse every k elements" | k-group reversal |
| "reverse nodes in even-length groups" | conditional group reversal |
| "rotate a linked list by k" | rotation = reconnect, no flip |
| "in-place" + linked list | O(1) space = pointer manipulation |
| "do not use extra space" | no stack, no recursion = 3-pointer approach |
Brute force approach: copy all values into an array, reverse the array, rewrite node values.
β This uses O(n) extra space.
β The interviewer says "in-place" or "O(1) space" β you cannot do this.
β You must flip the .next pointers directly = In-place Reversal pattern.
If the problem involves reordering nodes (not values) of a linked list β this pattern.
Signal 1 β The problem mentions "reverse" + "linked list" together
Almost a guaranteed match. 95% of reverse + linked list problems use this exact 3-pointer technique.
Signal 2 β You need O(1) extra space
No array copy. No stack. No recursion. Only constant-space pointer manipulation possible.
Signal 3 β You are asked to reverse a segment, not the full list
Sub-list reversal = same core loop, just add skip-to-start and reconnect logic around it.
Signal 4 β The problem says "groups of k" or "every k nodes"
K-group reversal = run the core loop in chunks of k, repeatedly.
Signal 5 β The problem says "rotate" a linked list
Rotation is disguised reversal β find tail, connect to head, break at new position. No actual flipping needed.
Problem involves a Linked List?
β
βΌ
Does it ask to reverse order of nodes?
β
βββ YES β Is it the full list?
β βββ YES β Problem 1 (full reversal)
β βββ NO β Is it a sub-list [p,q]?
β βββ YES β Problem 2 (sub-list reversal)
β βββ NO β Is it groups of k?
β βββ k=2 β Problem 3 (pairs)
β βββ k=N β Problem 4 (k-groups)
β βββ conditional β Problem 5 (even groups)
β
βββ NO β Does it ask to "rotate" the list?
βββ YES β Problem 6 (rotation)
Every node in a singly linked list has one .next pointer that points forward. In-place reversal means flipping those pointers to point backward β one node at a time β using only 3 pointer variables and no extra data structure.
Before: 1 β 2 β 3 β 4 β 5 β null
After: 5 β 4 β 3 β 2 β 1 β null
Why "in-place"?
You are not creating new nodes. You are not copying values. You are only changing where .next points. Memory usage = O(1) regardless of list size.
Core mechanic β one iteration:
prev β curr β next β (rest)
β
flip this arrow: curr.next = prev
When you write curr.next = prev, you lose access to the rest of the list. That is why you must save curr.next into a temporary next variable BEFORE flipping. This is the single most important thing to understand about this pattern.
Rule 1 β Always save next BEFORE flipping
// WRONG β you lose the rest of the list
curr.next = prev;
next = curr.next; // curr.next is now prev, not the original next β BUG
// CORRECT β save first, then flip
next = curr.next; // save the rest of the list
curr.next = prev; // now safe to flipRule 2 β After reversing a segment, the original head becomes the tail
// When you reverse [A β B β C] it becomes [C β B β A]
// The node you passed in as `head` (A) is now the TAIL.
// Use this to reconnect: headOfSegment.next = nextSegmentStart;
ListNode segmentTail = segmentHead; // save before reversing
ListNode newHead = reverse(segmentHead, count);
segmentTail.next = nextPart; // reconnect using the now-tailRule 3 β For sub-list problems, always save the node BEFORE the reversal starts
// You need `beforeP` to reconnect the left part to the reversed segment.
// Walk to position (p-1) and save that node.
ListNode beforeP = null;
ListNode curr = head;
for (int i = 1; i < p; i++) {
beforeP = curr;
curr = curr.next;
}
// After reversal: beforeP.next = newHead of reversed segmentAsk these two questions to identify exactly which variant you need.
| Answer | Variant |
|---|---|
| The entire list | Full reversal |
| A fixed window from index p to q | Sub-list reversal |
| Every 2 nodes (pairs) | Pair reversal (k=2) |
| Every k nodes | K-group reversal |
| Only even-length groups | Conditional reversal |
| None β just reposition the tail | Rotation |
| Answer | What it means |
|---|---|
| Nothing β one continuous reversal | Full reversal or sub-list |
| Advance k nodes, reverse k nodes, repeat | K-group reversal |
| Check group size before reversing | Conditional (even groups) |
| Find tail, connect head, break link | Rotation |
Decision shortcut:
- "reverse all" β full reversal
- "reverse [p..q]" β skip + partial reversal + reconnect
- "groups of k" β loop the reversal k at a time
- "rotate" β find tail + reconnect, no flip
- "even groups" β reverse only when group size is even
Common core:
prev,curr,next+ 4-line flip loop
What differs: how much you reverse, and how you reconnect
| Variant | Setup before loop | Loop reverses | Reconnection after |
|---|---|---|---|
| Full reversal | prev=null, curr=head |
All nodes | None β return prev |
| Sub-list [p,q] | Walk to p-1, save beforeP and tailOfSegment |
(q-p+1) nodes | beforeP.next = newHead and tailOfSegment.next = curr |
| Pairs (k=2) | dummy β head, work on pairs |
2 nodes at a time | Connect each pair to next pair |
| K-groups | Check k nodes exist, save segment tail | k nodes at a time | Connect segment tail to next group's new head |
| Even groups | Track group number and size | Only even-sized groups | Skip odd groups, reverse even groups, reconnect |
| Rotation | Find length and tail | Nothing reversed | tail.next = head, break at (len - k%len) |
// βββ NODE DEFINITION ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class ListNode {
int val;
ListNode next;
ListNode(int val) { this.val = val; }
}
// βββ CORE 4-LINE FLIP (memorise this β every problem uses it) βββββββββββββββββ
// Reverses `count` nodes starting from `head`.
// Returns: new head of the reversed segment.
// After return: original `head` is now the TAIL of the reversed segment.
private ListNode reverseSegment(ListNode head, int count) {
ListNode prev = null;
ListNode curr = head;
while (curr != null && count > 0) {
ListNode next = curr.next; // Step 1: SAVE β never lose the rest
curr.next = prev; // Step 2: FLIP β reverse the arrow
prev = curr; // Step 3: ADVANCE prev forward
curr = next; // Step 4: ADVANCE curr forward
count--;
}
// prev = new head of reversed segment
// curr = first node AFTER the reversed segment (use for reconnection)
return prev;
}
// βββ HOW TO STITCH SEGMENTS TOGETHER βββββββββββββββββββββββββββββββββββββββββ
// Pattern for connecting reversed chunk back to the rest of the list:
//
// ListNode segmentTail = segmentStart; // save tail BEFORE reversing
// ListNode newHead = reverseSegment(segmentStart, k);
// prevGroupTail.next = newHead; // left side connects to new head
// segmentTail.next = curr; // new tail connects to remainder
// prevGroupTail = segmentTail; // advance the "left boundary"
//
// βββ DUMMY NODE TRICK (for problems where head might change) ββββββββββββββββββ
// ListNode dummy = new ListNode(0);
// dummy.next = head;
// // Work with dummy as the anchor.
// // Return dummy.next as the final head.SIGNAL IN PROBLEM β VARIANT β KEY TRICK
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
"reverse the whole list" β Full reversal β prev=null, loop, return prev
"reverse between p and q" β Sub-list reversal β skip (p-1), save beforeP, reverse (q-p+1), reconnect
"swap pairs / reverse in pairs" β k=2 group β swap adjacent pairs, advance by 2
"reverse every k nodes" β k-group reversal β check k exist, reverse, reconnect, repeat
"reverse even-length groups" β Conditional reversal β check actual group size, reverse only if even
"rotate right by k" β Rotation β find tail+len, k%=len, connect tailβhead, break at (len-k)
The 4-line flip β burn this into memory:
ListNode next = curr.next; // SAVE
curr.next = prev; // FLIP
prev = curr; // ADVANCE prev
curr = next; // ADVANCE currReconnection pattern β always the same structure:
ListNode segmentTail = segmentStart; // save tail BEFORE reversing
ListNode newHead = reverse(segmentStart, k); // reverse returns new head
prevTail.next = newHead; // left β new head
segmentTail.next = curr; // new tail β right
prevTail = segmentTail; // slide the left boundaryDummy node β use whenever head might change:
ListNode dummy = new ListNode(0);
dummy.next = head;
// ... work ...
return dummy.next;// BUG β curr.next is already prev after flip, not the original next
curr.next = prev;
next = curr.next; // this is now prev, not the original next β list is lost
// FIX β always save BEFORE flipping
next = curr.next; // save first
curr.next = prev; // then flip// BUG β reversed segment floats with null at its tail
ListNode newHead = reverse(start, k);
prevTail.next = newHead;
// start.next is still null from the reversal β list is broken
// FIX β always reconnect the tail
ListNode segTail = start; // save BEFORE reversing
ListNode newHead = reverse(start, k);
prevTail.next = newHead;
segTail.next = curr; // tail connects to the rest// BUG β k=7 on a list of length 5 β 7 full rotations = wasteful
rotateRight(head, 7); // without mod, you walk 7 nodes back
// FIX β always reduce k first
k = k % len;
if (k == 0) return head; // no-op after reduction// BUG β for p=3, you need to stop at node 2 (beforeP)
// Loop condition must be i < p, not i <= p
for (int i = 1; i <= p; i++) // WRONG β goes one too far
beforeP = curr;
for (int i = 1; i < p; i++) // CORRECT β stops at p-1
beforeP = curr;// BUG β reverses the last group even if it has fewer than k nodes
while (curr != null) {
reverse(curr, k); // WRONG β partial group gets reversed
// FIX β check k nodes exist first
ListNode check = prevGroupTail;
for (int i = 0; i < k; i++) {
check = check.next;
if (check == null) return dummy.next; // stop β fewer than k nodes
}// BUG β if the head is part of a reversed segment, returning `head` is wrong
return head; // head might now be in the middle of the list
// FIX β always use dummy, return dummy.next
ListNode dummy = new ListNode(0);
dummy.next = head;
// ...
return dummy.next; // always correct regardless of how head changes| Problem | Time | Space | Notes |
|---|---|---|---|
| Reverse whole list | O(n) | O(1) | single pass |
| Reverse sub-list [p,q] | O(n) | O(1) | walk to p + reverse segment |
| Reverse in pairs | O(n) | O(1) | one pass, swap each pair |
| Reverse k-groups | O(n) | O(1) | each node processed once |
| Reverse even-length groups | O(n) | O(1) | each node visited once |
| Rotate by k | O(n) | O(1) | find tail + reconnect |
| Reverse alternate k | O(n) | O(1) | one pass |
| Palindrome check | O(n) | O(1) | find middle + reverse half + compare |
General rule:
- All in-place reversal problems run in O(n) time β every node is touched a constant number of times.
- Space is always O(1) β only
prev,curr,nextare used regardless of list size. - If you see O(n) space in a reversal solution (e.g., using a stack), it can always be reduced to O(1) with the 3-pointer technique.
In-place Reversal is one of the most tested LinkedList patterns in interviews. Master the 4-line flip and the reconnection template β everything else is just where you start and stop.