Reversal variants
Reversal variants of the three-pointer technique: full reversal (iterative and recursive), reversing a range, reversing in groups of k and the reorder-list pattern that combines middle, reverse and merge.
Learning goals
- Reverse iteratively and recursively
- Reverse a sub-range with a dummy node
- Reverse nodes in groups of k
- Reorder a list by splicing two halves
Prerequisites
Full reverse
Iterative reversal keeps prev, curr and next: save curr.next, point curr.next at prev, advance both. It runs in O(n) time and O(1) space. The new head is the old tail, which is where prev ends.
Watch each arrow flip in the animation.
1static Node reverse(Node head) {2Node prev = null, curr = head;3while (curr != null) {4Node next = curr.next;5curr.next = prev;6prev = curr; curr = next;7}8return prev;9}
Reversing a linked list by flipping pointers
Interactive · edit the inputlinked list · 5 nodes
Reversal flips every next pointer. No values move: only the arrows change, in O(n) time and O(1) space.
1static Node reverse(Node head) {2Node prev = null, curr = head;3while (curr != null) {4Node next = curr.next; // save the rest of the list5curr.next = prev; // flip the pointer6prev = curr; curr = next; // step forward7}8return prev; // prev is the new head9}10static Node reverseBetween(Node head, int left, int right) { // 1-based positions11Node dummy = new Node(0); dummy.next = head; Node pre = dummy;12for (int i = 1; i < left; i++) pre = pre.next; // node before the segment13Node curr = pre.next;14for (int i = 0; i < right - left; i++) {15Node next = curr.next;16curr.next = next.next; next.next = pre.next; pre.next = next; // move next to the front of the segment17}18return dummy.next;19}20static Node reverseKGroup(Node head, int k) { // groups shorter than k stay as they are21Node dummy = new Node(0); dummy.next = head; Node groupPrev = dummy;22while (true) {23Node kth = groupPrev; for (int i = 0; i < k && kth != null; i++) kth = kth.next;24if (kth == null) break; // fewer than k nodes left25Node groupStart = groupPrev.next, groupNext = kth.next, prev = groupNext, curr = groupStart;26while (curr != groupNext) { Node next = curr.next; curr.next = prev; prev = curr; curr = next; }27groupPrev.next = kth; groupPrev = groupStart;28}29return dummy.next;30}
Live state
head
1
Execution trace (dry run table)Show 17 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | head=1 | Reversal flips every next pointer. No values move: only the arrows change, in O(n) time and O(1) space. |
| 2 | L4 | head=1 | Save next = 2 before changing curr.next, or the rest of the list would be lost. |
| 3 | L5 | head=1 | curr.next = prev: node 1 now points to null (it becomes the tail). |
| 4 | L6 | head=1 | Step both pointers forward. |
| 5 | L4 | head=1 | Save next = 3 before changing curr.next, or the rest of the list would be lost. |
| 6 | L5 | head=1 | curr.next = prev: node 2 now points back at 1. |
| 7 | L6 | head=1 | Step both pointers forward. |
| 8 | L4 | head=1 | Save next = 4 before changing curr.next, or the rest of the list would be lost. |
| 9 | L5 | head=1 | curr.next = prev: node 3 now points back at 2. |
| 10 | L6 | head=1 | Step both pointers forward. |
| 11 | L4 | head=1 | Save next = 5 before changing curr.next, or the rest of the list would be lost. |
| 12 | L5 | head=1 | curr.next = prev: node 4 now points back at 3. |
| 13 | L6 | head=1 | Step both pointers forward. |
| 14 | L4 | head=1 | Save next = null before changing curr.next, or the rest of the list would be lost. |
| 15 | L5 | head=1 | curr.next = prev: node 5 now points back at 4. |
| 16 | L6 | head=1 | Step both pointers forward. |
| 17 | L8 | head=5 return=[5, 4, 3, 2, 1] | Return prev as the new head: [5, 4, 3, 2, 1]. |
Reverse recursively
The recursive version reverses everything after the head and then makes the head's successor point back to the head. The base case is an empty or single-node list. It is short but uses O(n) call-stack space, so very long lists risk StackOverflowError; prefer the iterative version for large inputs.
The iterative version in the animation does the same relinking without the recursion.
1static Node reverseRecursive(Node head) {2if (head == null || head.next == null) return head;3Node newHead = reverseRecursive(head.next); // reverse the rest4head.next.next = head; // the old successor now points back at head5head.next = null; // head becomes the tail6return newHead;7}
Reversing a linked list by flipping pointers
Interactive · edit the inputlinked list · 5 nodes
Reversal flips every next pointer. No values move: only the arrows change, in O(n) time and O(1) space.
1static Node reverse(Node head) {2Node prev = null, curr = head;3while (curr != null) {4Node next = curr.next; // save the rest of the list5curr.next = prev; // flip the pointer6prev = curr; curr = next; // step forward7}8return prev; // prev is the new head9}10static Node reverseBetween(Node head, int left, int right) { // 1-based positions11Node dummy = new Node(0); dummy.next = head; Node pre = dummy;12for (int i = 1; i < left; i++) pre = pre.next; // node before the segment13Node curr = pre.next;14for (int i = 0; i < right - left; i++) {15Node next = curr.next;16curr.next = next.next; next.next = pre.next; pre.next = next; // move next to the front of the segment17}18return dummy.next;19}20static Node reverseKGroup(Node head, int k) { // groups shorter than k stay as they are21Node dummy = new Node(0); dummy.next = head; Node groupPrev = dummy;22while (true) {23Node kth = groupPrev; for (int i = 0; i < k && kth != null; i++) kth = kth.next;24if (kth == null) break; // fewer than k nodes left25Node groupStart = groupPrev.next, groupNext = kth.next, prev = groupNext, curr = groupStart;26while (curr != groupNext) { Node next = curr.next; curr.next = prev; prev = curr; curr = next; }27groupPrev.next = kth; groupPrev = groupStart;28}29return dummy.next;30}
Live state
head
1
Execution trace (dry run table)Show 17 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | head=1 | Reversal flips every next pointer. No values move: only the arrows change, in O(n) time and O(1) space. |
| 2 | L4 | head=1 | Save next = 2 before changing curr.next, or the rest of the list would be lost. |
| 3 | L5 | head=1 | curr.next = prev: node 1 now points to null (it becomes the tail). |
| 4 | L6 | head=1 | Step both pointers forward. |
| 5 | L4 | head=1 | Save next = 3 before changing curr.next, or the rest of the list would be lost. |
| 6 | L5 | head=1 | curr.next = prev: node 2 now points back at 1. |
| 7 | L6 | head=1 | Step both pointers forward. |
| 8 | L4 | head=1 | Save next = 4 before changing curr.next, or the rest of the list would be lost. |
| 9 | L5 | head=1 | curr.next = prev: node 3 now points back at 2. |
| 10 | L6 | head=1 | Step both pointers forward. |
| 11 | L4 | head=1 | Save next = 5 before changing curr.next, or the rest of the list would be lost. |
| 12 | L5 | head=1 | curr.next = prev: node 4 now points back at 3. |
| 13 | L6 | head=1 | Step both pointers forward. |
| 14 | L4 | head=1 | Save next = null before changing curr.next, or the rest of the list would be lost. |
| 15 | L5 | head=1 | curr.next = prev: node 5 now points back at 4. |
| 16 | L6 | head=1 | Step both pointers forward. |
| 17 | L8 | head=5 return=[5, 4, 3, 2, 1] | Return prev as the new head: [5, 4, 3, 2, 1]. |
Reverse between indices
To reverse only positions left through right (1-based), walk to the node before the segment (pre, using a dummy head when left is 1), then repeatedly move the node after curr to the front of the segment: curr.next = next.next; next.next = pre.next; pre.next = next. It is one pass, O(n), and the rest of the list stays linked.
Choose Reverse positions left..right and try 2..4.
1static Node reverseBetween(Node head, int left, int right) {2Node dummy = new Node(0); dummy.next = head;3Node pre = dummy;4for (int i = 1; i < left; i++) pre = pre.next;5Node curr = pre.next;6for (int i = 0; i < right - left; i++) {7Node next = curr.next;8curr.next = next.next;9next.next = pre.next;10pre.next = next;11}12return dummy.next;13}
Reversing a linked list by flipping pointers
Interactive · edit the inputlinked list · 5 nodes
Reversal flips every next pointer. No values move: only the arrows change, in O(n) time and O(1) space.
1static Node reverse(Node head) {2Node prev = null, curr = head;3while (curr != null) {4Node next = curr.next; // save the rest of the list5curr.next = prev; // flip the pointer6prev = curr; curr = next; // step forward7}8return prev; // prev is the new head9}10static Node reverseBetween(Node head, int left, int right) { // 1-based positions11Node dummy = new Node(0); dummy.next = head; Node pre = dummy;12for (int i = 1; i < left; i++) pre = pre.next; // node before the segment13Node curr = pre.next;14for (int i = 0; i < right - left; i++) {15Node next = curr.next;16curr.next = next.next; next.next = pre.next; pre.next = next; // move next to the front of the segment17}18return dummy.next;19}20static Node reverseKGroup(Node head, int k) { // groups shorter than k stay as they are21Node dummy = new Node(0); dummy.next = head; Node groupPrev = dummy;22while (true) {23Node kth = groupPrev; for (int i = 0; i < k && kth != null; i++) kth = kth.next;24if (kth == null) break; // fewer than k nodes left25Node groupStart = groupPrev.next, groupNext = kth.next, prev = groupNext, curr = groupStart;26while (curr != groupNext) { Node next = curr.next; curr.next = prev; prev = curr; curr = next; }27groupPrev.next = kth; groupPrev = groupStart;28}29return dummy.next;30}
Live state
head
1
Execution trace (dry run table)Show 17 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | head=1 | Reversal flips every next pointer. No values move: only the arrows change, in O(n) time and O(1) space. |
| 2 | L4 | head=1 | Save next = 2 before changing curr.next, or the rest of the list would be lost. |
| 3 | L5 | head=1 | curr.next = prev: node 1 now points to null (it becomes the tail). |
| 4 | L6 | head=1 | Step both pointers forward. |
| 5 | L4 | head=1 | Save next = 3 before changing curr.next, or the rest of the list would be lost. |
| 6 | L5 | head=1 | curr.next = prev: node 2 now points back at 1. |
| 7 | L6 | head=1 | Step both pointers forward. |
| 8 | L4 | head=1 | Save next = 4 before changing curr.next, or the rest of the list would be lost. |
| 9 | L5 | head=1 | curr.next = prev: node 3 now points back at 2. |
| 10 | L6 | head=1 | Step both pointers forward. |
| 11 | L4 | head=1 | Save next = 5 before changing curr.next, or the rest of the list would be lost. |
| 12 | L5 | head=1 | curr.next = prev: node 4 now points back at 3. |
| 13 | L6 | head=1 | Step both pointers forward. |
| 14 | L4 | head=1 | Save next = null before changing curr.next, or the rest of the list would be lost. |
| 15 | L5 | head=1 | curr.next = prev: node 5 now points back at 4. |
| 16 | L6 | head=1 | Step both pointers forward. |
| 17 | L8 | head=5 return=[5, 4, 3, 2, 1] | Return prev as the new head: [5, 4, 3, 2, 1]. |
Reverse in k-groups
Reverse each consecutive group of k nodes and leave a final group shorter than k unchanged. For each group first check that k nodes remain, reverse the group with the usual three pointers (making the first node point at the node after the group), then reconnect the previous group to the new group head. O(n) time, O(1) space.
Try groups of 2 and groups of 3 on the presets.
1static Node reverseKGroup(Node head, int k) {2Node dummy = new Node(0); dummy.next = head;3Node groupPrev = dummy;4while (true) {5Node kth = groupPrev;6for (int i = 0; i < k && kth != null; i++) kth = kth.next;7if (kth == null) break; // fewer than k nodes remain8Node groupStart = groupPrev.next, groupNext = kth.next, prev = groupNext, curr = groupStart;9while (curr != groupNext) { Node next = curr.next; curr.next = prev; prev = curr; curr = next; }10groupPrev.next = kth;11groupPrev = groupStart;12}13return dummy.next;14}
Reversing a linked list by flipping pointers
Interactive · edit the inputlinked list · 5 nodes
Reversal flips every next pointer. No values move: only the arrows change, in O(n) time and O(1) space.
1static Node reverse(Node head) {2Node prev = null, curr = head;3while (curr != null) {4Node next = curr.next; // save the rest of the list5curr.next = prev; // flip the pointer6prev = curr; curr = next; // step forward7}8return prev; // prev is the new head9}10static Node reverseBetween(Node head, int left, int right) { // 1-based positions11Node dummy = new Node(0); dummy.next = head; Node pre = dummy;12for (int i = 1; i < left; i++) pre = pre.next; // node before the segment13Node curr = pre.next;14for (int i = 0; i < right - left; i++) {15Node next = curr.next;16curr.next = next.next; next.next = pre.next; pre.next = next; // move next to the front of the segment17}18return dummy.next;19}20static Node reverseKGroup(Node head, int k) { // groups shorter than k stay as they are21Node dummy = new Node(0); dummy.next = head; Node groupPrev = dummy;22while (true) {23Node kth = groupPrev; for (int i = 0; i < k && kth != null; i++) kth = kth.next;24if (kth == null) break; // fewer than k nodes left25Node groupStart = groupPrev.next, groupNext = kth.next, prev = groupNext, curr = groupStart;26while (curr != groupNext) { Node next = curr.next; curr.next = prev; prev = curr; curr = next; }27groupPrev.next = kth; groupPrev = groupStart;28}29return dummy.next;30}
Live state
head
1
Execution trace (dry run table)Show 17 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | head=1 | Reversal flips every next pointer. No values move: only the arrows change, in O(n) time and O(1) space. |
| 2 | L4 | head=1 | Save next = 2 before changing curr.next, or the rest of the list would be lost. |
| 3 | L5 | head=1 | curr.next = prev: node 1 now points to null (it becomes the tail). |
| 4 | L6 | head=1 | Step both pointers forward. |
| 5 | L4 | head=1 | Save next = 3 before changing curr.next, or the rest of the list would be lost. |
| 6 | L5 | head=1 | curr.next = prev: node 2 now points back at 1. |
| 7 | L6 | head=1 | Step both pointers forward. |
| 8 | L4 | head=1 | Save next = 4 before changing curr.next, or the rest of the list would be lost. |
| 9 | L5 | head=1 | curr.next = prev: node 3 now points back at 2. |
| 10 | L6 | head=1 | Step both pointers forward. |
| 11 | L4 | head=1 | Save next = 5 before changing curr.next, or the rest of the list would be lost. |
| 12 | L5 | head=1 | curr.next = prev: node 4 now points back at 3. |
| 13 | L6 | head=1 | Step both pointers forward. |
| 14 | L4 | head=1 | Save next = null before changing curr.next, or the rest of the list would be lost. |
| 15 | L5 | head=1 | curr.next = prev: node 5 now points back at 4. |
| 16 | L6 | head=1 | Step both pointers forward. |
| 17 | L8 | head=5 return=[5, 4, 3, 2, 1] | Return prev as the new head: [5, 4, 3, 2, 1]. |
Reorder list
Reorder L0 → L1 → … → Ln into L0 → Ln → L1 → Ln-1 → …: find the middle, cut the list there, reverse the second half and weave the two halves together by alternating nodes. Each step is O(n), so the whole is O(n) time and O(1) space.
Switch the animation to Reorder and watch the second half being reversed and then interleaved.
1static void reorder(Node head) {2Node slow = head, fast = head;3while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; }4Node prev = null, curr = slow.next; slow.next = null; // cut after the first half5while (curr != null) { Node next = curr.next; curr.next = prev; prev = curr; curr = next; }6for (Node a = head, b = prev; b != null; ) { // weave7Node an = a.next, bn = b.next;8a.next = b; b.next = an;9a = an; b = bn;10}11}
Palindrome list and reorder list (middle, reverse, combine)
Interactive · edit the inputlinked list · 5 nodes
Plan: find the middle, reverse the second half in place, then compare the two halves from the outside in. O(n) time, O(1) space.
1static boolean isPalindrome(Node head) {2Node slow = head, fast = head;3while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } // slow = middle4Node prev = null, curr = slow;5while (curr != null) { Node next = curr.next; curr.next = prev; prev = curr; curr = next; } // reverse the second half6for (Node a = head, b = prev; b != null; a = a.next, b = b.next)7if (a.val != b.val) return false;8return true; // callers that still need the list should re-reverse the second half9}10static void reorder(Node head) { // L0 L1 ... Ln -> L0 Ln L1 Ln-1 ...11Node slow = head, fast = head;12while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; }13Node prev = null, curr = slow.next; slow.next = null; // cut after the first half14while (curr != null) { Node next = curr.next; curr.next = prev; prev = curr; curr = next; }15for (Node a = head, b = prev; b != null; ) { // weave the two halves together16Node an = a.next, bn = b.next; a.next = b; b.next = an; a = an; b = bn;17}18}
Live state
Execution trace (dry run table)Show 11 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | Plan: find the middle, reverse the second half in place, then compare the two halves from the outside in. O(n) time, O(1) space. | |
| 2 | L3 | slow=2 fast=3 | slow = 2, fast = 3. |
| 3 | L3 | slow=3 fast=1 | slow = 3, fast = 1. |
| 4 | L4 | Reverse the second half with the usual three-pointer loop. | |
| 5 | L5 | Flip 3 to point at null. | |
| 6 | L5 | Flip 2 to point at 3. | |
| 7 | L5 | Flip 1 to point at 2. | |
| 8 | L7 | a=1 b=1 | 1 = 1: the outer pair matches. |
| 9 | L7 | a=2 b=2 | 2 = 2: the outer pair matches. |
| 10 | L7 | a=3 b=3 | 3 = 3: the outer pair matches. |
| 11 | L8 | return=true | Every pair matched: the list is a palindrome. (The second half is still reversed; undo that if the list is used again.) |
Practice ladder
Now try it yourself
Easy
- Reverse 1 → 2 → 3 → 4 by hand
- What is the base case of the recursive reversal?
Medium
- Reverse positions 2 to 4 of 1 → 2 → 3 → 4 → 5
- Reverse 1 → 2 → 3 → 4 → 5 in groups of 2
Advanced
- Reverse alternate groups of k nodes
Key takeaways
- prev, curr, next: save the successor, flip the arrow, advance.
- A dummy head removes the left = 1 special case.
- Reorder = middle + reverse + weave, all in O(n) time and O(1) space.