Reversing LinkedList Nodes In-Place
1. Understanding In-Place Reversal Technique
The in-place reversal of a linked list is a fundamental algorithm pattern used to reverse the direction of links in a singly linked list without using extra space for another data structure.
| Concept | Description | Use Case |
|---|---|---|
| Pointer Manipulation | Reversing a linked list involves changing the direction of pointers from next to point to the previous node instead of the next node. |
Efficiently reversing a list in memory-constrained environments |
| Three-Pointer Technique | Uses three pointers: previous, current, and next to keep track of nodes during reversal. |
Standard approach for reversing singly linked lists |
Example: Reversing a LinkedList In-Place
class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
function reverseLinkedList(head) {
let previous = null;
let current = head;
while (current !== null) {
const next = current.next;
current.next = previous;
previous = current;
current = next;
}
return previous;
}
// Example usage:
const node3 = new ListNode(3);
const node2 = new ListNode(2, node3);
const node1 = new ListNode(1, node2);
const reversedHead = reverseLinkedList(node1);
Note: The function returns the new head of the reversed list, which was originally the last node.
2. Reversing Entire LinkedList
| Property | Description | Example |
|---|---|---|
| head | Pointer to the first node in the list | Node head = new Node(1); |
| next | Pointer to the next node in the list | current.next = previous; |
Example: Reversing a LinkedList In-Place
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode current = head;
while (current != null) {
ListNode nextTemp = current.next;
current.next = prev;
prev = current;
current = nextTemp;
}
return prev;
}
Note: The algorithm uses three pointers to reverse the links in-place, maintaining O(1) space complexity.
3. Reversing LinkedList Sublist
| Property | Description | Usage |
|---|---|---|
| head | Pointer to the first node in the list | Start of the reversal process |
| start | Index of the first node in sublist to reverse | Determines where reversal begins |
| end | Index of the last node in sublist to reverse | Determines where reversal ends |
Example: Reversing a Sublist
class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
function reverseSublist(head, start, end) {
if (!head || start === end) return head;
let dummy = new ListNode(0);
dummy.next = head;
let prev = dummy;
for (let i = 0; i < start; i++) {
prev = prev.next;
}
let current = prev.next;
for (let i = start; i < end; i++) {
let nextNode = current.next;
current.next = nextNode.next;
nextNode.next = prev.next;
prev.next = nextNode;
}
return dummy.next;
}
// Example usage:
const list = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4, new ListNode(5)))));
const result = reverseSublist(list, 1, 3);
// Result: [1, 4, 3, 2, 5]
Note: The indices are 0-based. Reversing from index 1 to 3 means reversing nodes at positions 1, 2, and 3.
4. Reversing K-Group Nodes
| Property | Description | Example |
|---|---|---|
| k | Number of nodes to reverse in each group | 3 |
| head | Head of the linked list | [1,2,3,4,5] |
Example: Reversing K-Group Nodes
class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
function reverseKGroup(head, k) {
const dummy = new ListNode(0);
dummy.next = head;
let prevGroupEnd = dummy;
while (true) {
let groupStart = prevGroupEnd.next;
let groupEnd = groupStart;
for (let i = 0; i < k - 1; i++) {
if (!groupEnd) return dummy.next;
groupEnd = groupEnd.next;
}
if (!groupEnd) return dummy.next;
const nextGroupStart = groupEnd.next;
let prev = null;
let current = groupStart;
while (current !== nextGroupStart) {
const next = current.next;
current.next = prev;
prev = current;
current = next;
}
groupStart.next = nextGroupStart;
prevGroupEnd.next = groupEnd;
prevGroupEnd = groupStart;
}
}
// Example usage:
const head = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4, new ListNode(5)))));
const result = reverseKGroup(head, 2);
// Result: [2,1,4,3,5]
Note: If the number of nodes is not a multiple of k, the remaining nodes at the end should remain as they are.
5. Reversing Alternating K-Groups
Reversing alternating k-groups involves reversing nodes in groups of size k, but only reversing every other group (i.e., reverse 1st group, skip 2nd, reverse 3rd, and so on).
| Group | Nodes in Group | Action | Result After Reversal |
|---|---|---|---|
| Group 1 | 1 → 2 → 3 → 4 |
Reverse | 4 → 3 → 2 → 1 |
| Group 2 | 5 → 6 → 7 → 8 |
Skip | 5 → 6 → 7 → 8 |
| Group 3 | 9 → 10 → 11 → 12 |
Reverse | 12 → 11 → 10 → 9 |
Example: Reversing Alternating K-Groups
class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
function reverseAlternatingKGroups(head, k) {
if (!head || k <= 1) return head;
let dummy = new ListNode(0);
dummy.next = head;
let prev = dummy;
let count = 0;
let current = head;
while (current) {
count++;
if (count % (2 * k) === 1) {
// Reverse the first k nodes in this group
let tail = reverseKNodes(current, k);
prev.next = tail;
current = tail;
} else if (count % (2 * k) === 0) {
// Skip the second k nodes in this group
let temp = current;
for (let i = 0; i < k; i++) {
if (!temp) break;
temp = temp.next;
}
prev.next = current;
current = temp;
} else {
// Move to next node
prev = current;
current = current.next;
}
}
return dummy.next;
}
function reverseKNodes(head, k) {
let prev = null;
let current = head;
for (let i = 0; i < k; i++) {
let next = current.next;
current.next = prev;
prev = current;
current = next;
}
head.next = current; // Connect the remaining list
return prev; // New head of reversed group
}
Note: This approach reverses every other k-group in the list, skipping the second group in each pair.
6. Rotating LinkedList by K Positions
| Property | Description | Example |
|---|---|---|
| k | Number of positions to rotate the list | 2 |
| head | Pointer to the first node in the list | 1 -> 2 -> 3 -> 4 -> 5 |
Example: Rotating a LinkedList by K=2
class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
function rotateRight(head, k) {
if (!head || !head.next || k === 0) return head;
let tail = head;
let len = 1;
while (tail.next) {
tail = tail.next;
len++;
}
k = k % len;
if (k === 0) return head;
tail.next = head; // Make it circular
let newTail = head;
for (let i = 0; i < len - k - 1; i++) {
newTail = newTail.next;
}
let newHead = newTail.next;
newTail.next = null;
return newHead;
}
// Example usage:
const list = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4, new ListNode(5)))));
const rotated = rotateRight(list, 2);
// Result: 4 -> 5 -> 1 -> 2 -> 3
Note: The rotation is done in-place, modifying the links between nodes without using extra space for another list.
7. Swapping Nodes in Pairs
| Property | Description | Example |
|---|---|---|
| Algorithm | Swap adjacent nodes in pairs without modifying node values | 1 -> 2 -> 3 -> 4 => 2 -> 1 -> 4 -> 3 |
| Time Complexity | O(n) - Traverse list once | |
| Space Complexity | O(1) - Only use a few pointers |
Example: Swapping Nodes in Pairs
class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
function swapPairs(head) {
const dummy = new ListNode(0);
dummy.next = head;
let prev = dummy;
while (prev.next && prev.next.next) {
const first = prev.next;
const second = prev.next.next;
// Swapping
first.next = second.next;
second.next = first;
prev.next = second;
// Move pointer forward
prev = first;
}
return dummy.next;
}
// Example usage:
const list = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4))));
const swapped = swapPairs(list);
// Result: 2 -> 1 -> 4 -> 3
Note: The dummy node simplifies edge cases like an empty list or a single-node list.
8. Reversing Every K Elements
| Property | Description | Example |
|---|---|---|
| k | Number of nodes to reverse in each group | k = 3 |
| head | Head of the linked list | 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 |
Example: Reverse Every K Nodes
class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
function reverseKGroup(head, k) {
const dummy = new ListNode(0);
dummy.next = head;
let prevGroupEnd = dummy;
while (true) {
let groupStart = prevGroupEnd.next;
let groupEnd = groupStart;
// Find the end of the current group
for (let i = 0; i < k - 1 && groupEnd !== null; i++) {
groupEnd = groupEnd.next;
}
if (groupEnd === null) break;
const nextGroupStart = groupEnd.next;
// Reverse the current group
let prev = null;
let current = groupStart;
while (current !== nextGroupStart) {
const next = current.next;
current.next = prev;
prev = current;
current = next;
}
// Reconnect the reversed group
prevGroupEnd.next = groupEnd;
groupStart.next = nextGroupStart;
prevGroupEnd = groupStart;
}
return dummy.next;
}
// Example usage:
const list = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4, new ListNode(5, new ListNode(6))))));
const result = reverseKGroup(list, 2);
// Result: 2 -> 1 -> 4 -> 3 -> 6 -> 5
Note: If the number of nodes is not a multiple of k, the remaining nodes at the end are left as they are.
9. Reordering LinkedList
| Operation | Description | Time Complexity | Space Complexity |
|---|---|---|---|
| Reorder List | Reorders a singly linked list such that nodes are arranged in the pattern: L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → ... | O(n) |
O(1) |
Example: Reordering a LinkedList
class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
function reorderList(head) {
if (!head || !head.next) return;
// Step 1: Find the middle of the list
let slow = head;
let fast = head;
while (fast.next && fast.next.next) {
slow = slow.next;
fast = fast.next.next;
}
// Step 2: Reverse the second half
let prev = null;
let curr = slow.next;
slow.next = null; // Break the list
while (curr) {
let next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
// Step 3: Merge the two halves
let firstHalf = head;
let secondHalf = prev;
while (secondHalf) {
let next1 = firstHalf.next;
let next2 = secondHalf.next;
firstHalf.next = secondHalf;
secondHalf.next = next1;
firstHalf = next1;
secondHalf = next2;
}
}
// Example usage:
const list = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4))));
reorderList(list);
// Result: 1 → 4 → 2 → 3
10. Reversing LinkedList Second Half
| Step | Description | Action | Code |
|---|---|---|---|
| 1 | Find the middle of the list using slow and fast pointers | Use two pointers: slow moves one step, fast moves two steps |
|
| 2 | Reverse the second half starting from slow |
Reverse the linked list from slow to end |
|
| 3 | Reconnect the first half to the reversed second half | Adjust pointers so that the first half connects to the reversed second half | |
Example: Reversing Second Half of LinkedList
class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
function reverseSecondHalf(head) {
if (!head || !head.next) return head;
let slow = head;
let fast = head;
// Step 1: Find the middle
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
}
// Step 2: Reverse the second half
let prev = null;
let current = slow;
while (current) {
let next = current.next;
current.next = prev;
prev = current;
current = next;
}
// Step 3: Reconnect the halves
let firstHalf = head;
let secondHalfReversed = prev;
while (secondHalfReversed) {
let temp = firstHalf.next;
firstHalf.next = secondHalfReversed;
secondHalfReversed = secondHalfReversed.next;
firstHalf.next.next = temp;
firstHalf = temp;
}
return head;
}
// Example usage:
let node1 = new ListNode(1);
let node2 = new ListNode(2);
let node3 = new ListNode(3);
let node4 = new ListNode(4);
let node5 = new ListNode(5);
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
let result = reverseSecondHalf(node1);
// Result: [1, 4, 2, 3, 5]