Reverse linked lists or portions of them without extra space using pointer manipulation.
The In-Place Reversal pattern reverses a linked list (or a portion of it) by redirecting pointers rather than creating new nodes. Instead of building a new list, you simply flip the direction of each arrow.
The technique uses three pointers — prev, curr, and next — to walk through the list and reverse each link one at a time.
Original list: arrows point forward. Reversed list: every arrow points backward.
Core principle: You never allocate new nodes. You simply change where each node's .next pointer points. This gives you O(1) space and O(n) time — the optimal solution for any linked list reversal problem.
Reach for the in-place reversal pattern whenever you see these signals:
Why not use a stack? You could push all nodes onto a stack and rebuild, but that uses O(n) extra space. In-place reversal achieves the same result with O(1) space by manipulating pointers directly. Interviewers specifically look for this optimization.
The reversal follows a simple four-step dance at each node. Think of it as "save, flip, advance, advance":
next = curr.next — we are about to overwrite curr.next, so save it first.curr.next = prev — point current node backward instead of forward.prev = curr — prev moves forward to where curr is now.curr = next — curr moves to the saved next node.// The Core Reversal Template ListNode prev = null, curr = head; while (curr != null) { ListNode next = curr.next; // 1. save next curr.next = prev; // 2. flip the arrow prev = curr; // 3. advance prev curr = next; // 4. advance curr } return prev; // new head
Why save next first? The moment you execute curr.next = prev, you lose access to the rest of the original list. If you have not saved curr.next beforehand, you cannot move forward. This is the single most common mistake in linked list reversal.
The simplest variant: reverse every node from head to tail. This is the direct application of the core template with no modifications.
Given the head of a singly linked list, reverse the list, and return the reversed list.
class Solution { public ListNode reverseList(ListNode head) { ListNode prev = null; ListNode curr = head; while (curr != null) { ListNode next = curr.next; // save curr.next = prev; // flip prev = curr; // advance prev curr = next; // advance curr } return prev; // prev is the new head } }
Recursive alternative: You can also reverse recursively — recurse to the end, then set head.next.next = head on the way back. But the iterative version is preferred in interviews because it uses O(1) space instead of O(n) call stack space.
Reverse only the nodes from position m to position n, keeping the rest of the list intact. This requires three phases: navigate to position m, reverse the sublist, and reconnect the ends.
1 → 2 → 3 → 4 → 5 becomes 1 → 4 → 3 → 2 → 5
class Solution { public ListNode reverseBetween(ListNode head, int left, int right) { if (left == right) return head; // Dummy node simplifies edge cases when left == 1 ListNode dummy = new ListNode(0); dummy.next = head; // Phase 1: Navigate to position left-1 ListNode before = dummy; for (int i = 1; i < left; i++) before = before.next; // Phase 2: Reverse from left to right ListNode prev = null; ListNode curr = before.next; for (int i = 0; i < right - left + 1; i++) { ListNode next = curr.next; curr.next = prev; prev = curr; curr = next; } // Phase 3: Reconnect before.next.next = curr; // tail of reversed connects to right+1 before.next = prev; // before connects to new head of reversed return dummy.next; } }
The dummy node trick: When the reversal starts at position 1, there is no "before" node. A dummy node placed before the head eliminates this edge case entirely. After reversal, dummy.next always points to the correct new head.
Process k nodes at a time, reversing each group independently, then connecting all groups together. If fewer than k nodes remain at the end, leave them as-is.
1 → 2 → 3 → 4 → 5 → 6 → 7 with k=3
class Solution { public ListNode reverseKGroup(ListNode head, int k) { ListNode dummy = new ListNode(0); dummy.next = head; ListNode groupPrev = dummy; while (true) { // Check if k nodes remain ListNode check = groupPrev; for (int i = 0; i < k; i++) { check = check.next; if (check == null) return dummy.next; } // Reverse k nodes ListNode prev = null; ListNode curr = groupPrev.next; for (int i = 0; i < k; i++) { ListNode next = curr.next; curr.next = prev; prev = curr; curr = next; } // Connect reversed group ListNode groupTail = groupPrev.next; // was first, now last groupTail.next = curr; // connect to next group groupPrev.next = prev; // connect from previous // Move to next group groupPrev = groupTail; } } }
The connection pattern: After reversing a group, groupPrev.next (which was the group's first node) becomes the group's last node. So groupPrev.next gets reassigned to prev (new first), and the old first node's next points to curr (start of next group). Master this reconnection and the rest is just the basic reversal template.
These LeetCode problems are solved cleanly using the in-place reversal pattern.
#234 Palindrome Linked List is a great combination problem: use fast & slow pointers to find the middle, reverse the second half in-place, then compare both halves node by node. It combines two patterns in O(n) time and O(1) space.
Watch the three pointers dance through the list, flipping arrows one at a time.
Time O(n): Each node is visited exactly once. We do constant work per node (save, flip, advance, advance). For partial reversals, we still visit at most n nodes total.
Space O(1): We only use three pointer variables (prev, curr, next) regardless of list size. No arrays, stacks, or recursion needed.
K-Group variant: Still O(n) time. Each node is reversed exactly once. The "check if k nodes remain" step adds another pass, but it is at most n total checks across all groups.
Compare with recursive reversal: The recursive version has O(n) call stack space, making it O(n) space. The iterative in-place approach is strictly better in space complexity. This is why interviewers prefer the iterative solution.
Before flipping curr.next, save it. Losing access to the rest of the list is the most common reversal bug.
Save, flip, advance prev, advance curr. Four operations per node, same order every time. Memorize this rhythm.
For partial reversals, the old first node becomes the new last. Connect it to the node after the reversed section, and connect the node before to the new first (prev).
When reversal might change the head, add a dummy node before the list. It eliminates "what if left == 1" edge cases cleanly.
Flip the Arrows, Not the Nodes
Linked list reversal is not about moving data — it is about redirecting pointers. Three variables, four operations per node, constant space. Once you internalize the three-pointer dance, every reversal variant becomes a straightforward extension of the same core idea.