You're given two singly linked lists, list1
and list2
. Additionally, you are provided with two integers, a
and b
, where 0 <= a <= b < list1.length
. Your task is to remove the nodes from index a
to index b
(inclusive) from list1
, and then insert the entire list2
in their place. The result should be a single, well-formed linked list.
At first glance, this problem looks like a straightforward linked list manipulation. However, the challenge lies in carefully updating the pointers so that the nodes from a
to b
in list1
are removed and replaced by list2
, without losing any nodes or creating cycles.
A brute-force approach might involve copying values or creating new nodes, but the problem specifically asks us to rearrange existing nodes. So, the focus is on:
a
(preA
), so we can link it to the head of list2
.b
(postB
), so the tail of list2
can point to it.
The optimized approach is to traverse list1
only once to find preA
and postB
, then traverse list2
to find its tail. This ensures the solution is efficient and avoids unnecessary operations.
Let's break down the steps to solve the problem:
a
in list1
:
list1
to reach the node at position a - 1
. This node is called preA
.b
in list1
:
b + 1
. This node is called postB
.preA
to the head of list2
:
preA.next = list2
.list2
:
list2
until you reach its last node.list2
to postB
:
tail2.next = postB
.list1
:
a
is 0, in which case list2
becomes the new head.This approach is efficient and ensures that the modifications are done in-place, as required.
Let's walk through an example step by step.
Input:
list1 = 0 -> 1 -> 2 -> 3 -> 4 -> 5
list2 = 1000000 -> 1000001 -> 1000002
a = 3, b = 4
preA
: Node before index 3 is at index 2 (value: 2).
postB
: Node after index 4 is at index 5 (value: 5).
preA.next
to list2
: 2 -> 1000000.
list2
: 1000002.
tail2.next
to postB
: 1000002 -> 5.
0 -> 1 -> 2 -> 1000000 -> 1000001 -> 1000002 -> 5
At each step, we carefully update the pointers to avoid losing references to any nodes.
Brute-force approach:
If you tried to copy values or create new nodes, the time and space complexity would be O(N + M), where N and M are the lengths of list1
and list2
, due to the need to allocate new nodes.
Optimized approach (in-place pointer manipulation):
b
in list1
and M is the length of list2
. This is because we traverse up to b + 1
in list1
and the entire list2
to find its tail.
This problem demonstrates the importance of careful pointer manipulation in linked lists. By identifying the nodes before and after the segment to be replaced, and updating their pointers to seamlessly splice list2
into list1
, we achieve an efficient, in-place solution. The key insights are to avoid unnecessary node creation and to maintain references to all parts of the list throughout the process. This approach is both elegant and practical for real-world linked list operations.
# Definition for singly-linked list.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def mergeInBetween(self, list1: ListNode, a: int, b: int, list2: ListNode) -> ListNode:
# Step 1: Find preA (node before a)
preA = list1
for _ in range(a - 1):
preA = preA.next
# Step 2: Find postB (node after b)
postB = preA
for _ in range(b - a + 2):
postB = postB.next
# Step 3: Connect preA.next to list2
preA.next = list2
# Step 4: Find tail of list2
tail2 = list2
while tail2.next:
tail2 = tail2.next
# Step 5: Connect tail2.next to postB
tail2.next = postB
return list1
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeInBetween(ListNode* list1, int a, int b, ListNode* list2) {
// Step 1: Find preA (node before a)
ListNode* preA = list1;
for (int i = 0; i < a - 1; ++i) {
preA = preA->next;
}
// Step 2: Find postB (node after b)
ListNode* postB = preA;
for (int i = 0; i < b - a + 2; ++i) {
postB = postB->next;
}
// Step 3: Connect preA->next to list2
preA->next = list2;
// Step 4: Find tail of list2
ListNode* tail2 = list2;
while (tail2->next) {
tail2 = tail2->next;
}
// Step 5: Connect tail2->next to postB
tail2->next = postB;
return list1;
}
};
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) {
// Step 1: Find preA (node before a)
ListNode preA = list1;
for (int i = 0; i < a - 1; ++i) {
preA = preA.next;
}
// Step 2: Find postB (node after b)
ListNode postB = preA;
for (int i = 0; i < b - a + 2; ++i) {
postB = postB.next;
}
// Step 3: Connect preA.next to list2
preA.next = list2;
// Step 4: Find tail of list2
ListNode tail2 = list2;
while (tail2.next != null) {
tail2 = tail2.next;
}
// Step 5: Connect tail2.next to postB
tail2.next = postB;
return list1;
}
}
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} list1
* @param {number} a
* @param {number} b
* @param {ListNode} list2
* @return {ListNode}
*/
var mergeInBetween = function(list1, a, b, list2) {
// Step 1: Find preA (node before a)
let preA = list1;
for (let i = 0; i < a - 1; ++i) {
preA = preA.next;
}
// Step 2: Find postB (node after b)
let postB = preA;
for (let i = 0; i < b - a + 2; ++i) {
postB = postB.next;
}
// Step 3: Connect preA.next to list2
preA.next = list2;
// Step 4: Find tail of list2
let tail2 = list2;
while (tail2.next !== null) {
tail2 = tail2.next;
}
// Step 5: Connect tail2.next to postB
tail2.next = postB;
return list1;
};