1669. Merge In Between Linked Lists
Problem Description
You are given two linked lists: list1
and list2
of sizes n
and m
respectively.
Your task is to remove a range of nodes from list1
(specifically from the a
-th node to the b
-th node, inclusive) and replace them with the entire list2
.
The nodes in linked lists are 1-indexed, meaning:
- The
a
-th node is the node at positiona
(counting from 1) - The
b
-th node is the node at positionb
(counting from 1) - You need to remove all nodes from position
a
to positionb
(inclusive)
After removing these nodes, you should insert all nodes from list2
in their place, maintaining the connections properly.
For example, if list1
is [0,1,2,3,4,5]
, a = 3
, b = 4
, and list2
is [1000000,1000001,1000002]
:
- You would remove nodes at positions 3 and 4 (which have values 2 and 3)
- Insert
list2
in their place - The result would be
[0,1,1000000,1000001,1000002,4,5]
The function should return the head of the modified list1
after performing this merge operation.
Intuition
To solve this problem, we need to think about what connections we need to modify in the linked list structure.
The key insight is that we need to identify four critical points:
- The node just before position
a
(let's call it the "pre-removal" node) - The node at position
b
(the last node to be removed) - The head of
list2
(the start of what we're inserting) - The tail of
list2
(the end of what we're inserting)
Once we have these points, the operation becomes straightforward:
- Connect the "pre-removal" node to the head of
list2
- Connect the tail of
list2
to the node that comes after positionb
To find these positions, we can use two pointers traversing from the head of list1
:
- One pointer
p
movesa-1
steps to reach the node just before positiona
- Another pointer
q
movesb
steps to reach the node at positionb
The reason we move p
only a-1
steps is because we need to keep the connection point before the removal range. We need access to this node so we can redirect its next
pointer to list2
.
For q
, we move exactly b
steps because we need to know what comes after the last removed node (which is q.next
), so we can connect the tail of list2
to it.
Finally, we traverse list2
to find its tail node, then complete the connections. This approach modifies the original list1
in-place by splicing in list2
at the correct position.
Learn more about Linked List patterns.
Solution Approach
The solution uses a two-pointer simulation approach to perform the merge operation. Here's the step-by-step implementation:
Step 1: Initialize two pointers
p = q = list1
Both pointers p
and q
start at the head of list1
. These will help us locate the critical connection points.
Step 2: Position pointer p
before the removal range
for _ in range(a - 1):
p = p.next
Move p
forward (a-1)
times. After this loop, p
points to the node just before the a
-th node. This is crucial because we need to modify p.next
to point to list2
.
Step 3: Position pointer q
at the end of the removal range
for _ in range(b):
q = q.next
Move q
forward b
times from the head. After this loop, q
points to the b
-th node (the last node to be removed).
Step 4: Connect the pre-removal node to list2
p.next = list2
This disconnects the nodes from position a
to b
and starts the connection to list2
.
Step 5: Find the tail of list2
while p.next:
p = p.next
Since p.next
now points to list2
, we traverse through list2
until we reach its tail node (where p.next
becomes None
).
Step 6: Complete the connection
p.next = q.next
q.next = None
Connect the tail of list2
to the node that came after the b
-th node in the original list1
. Setting q.next = None
helps clean up the removed segment (though this is optional since those nodes are no longer reachable).
Step 7: Return the modified list
return list1
The head of list1
remains unchanged, so we return it as the result.
Time Complexity: O(a + b + m)
where m
is the length of list2
. We traverse to position a-1
, then to position b
, and finally through all nodes of list2
.
Space Complexity: O(1)
as we only use a constant amount of extra space for the pointers.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to illustrate the solution approach.
Given:
list1
: 10 → 20 → 30 → 40 → 50list2
: 100 → 200a = 2
,b = 3
(remove nodes at positions 2 and 3)
Goal: Remove nodes 20 and 30 from list1
and insert list2
in their place.
Step 1: Initialize pointers
p = q = list1 Both p and q point to node 10
Step 2: Position p before removal range (move a-1 = 1 time)
p moves 1 step: p now points to node 10 list1: 10 → 20 → 30 → 40 → 50 p
Step 3: Position q at end of removal range (move b = 3 times)
q moves 3 steps: 10 → 20 → 30 list1: 10 → 20 → 30 → 40 → 50 p q
Step 4: Connect p.next to list2
p.next = list2 (node 100) Now: 10 → 100 → 200 p (Nodes 20 and 30 are disconnected)
Step 5: Find tail of list2
Move p through list2: p: 10 → 100 → 200 p p (final position)
Step 6: Complete the connection
p.next = q.next (node 40) Final: 10 → 100 → 200 → 40 → 50
Result: The modified list is 10 → 100 → 200 → 40 → 50
The nodes at positions 2 and 3 (values 20 and 30) have been successfully replaced with the entire list2
(100 → 200).
Solution Implementation
1# Definition for singly-linked list.
2# class ListNode:
3# def __init__(self, val=0, next=None):
4# self.val = val
5# self.next = next
6
7class Solution:
8 def mergeInBetween(
9 self, list1: ListNode, a: int, b: int, list2: ListNode
10 ) -> ListNode:
11 """
12 Merge list2 into list1 by removing nodes from index a to b (inclusive)
13 and inserting list2 in their place.
14
15 Args:
16 list1: The first linked list
17 a: Starting index of nodes to remove (0-indexed)
18 b: Ending index of nodes to remove (0-indexed)
19 list2: The second linked list to insert
20
21 Returns:
22 The modified list1 with list2 merged in between
23 """
24 # Initialize two pointers to traverse list1
25 before_removal_node = list1 # Points to node just before index a
26 after_removal_node = list1 # Points to node at index b
27
28 # Move before_removal_node to the node just before index a
29 # (a-1 steps forward from the head)
30 for _ in range(a - 1):
31 before_removal_node = before_removal_node.next
32
33 # Move after_removal_node to the node at index b
34 # (b steps forward from the head)
35 for _ in range(b):
36 after_removal_node = after_removal_node.next
37
38 # Connect the node before index a to the head of list2
39 before_removal_node.next = list2
40
41 # Traverse to the end of list2
42 while before_removal_node.next:
43 before_removal_node = before_removal_node.next
44
45 # Connect the tail of list2 to the node after index b
46 before_removal_node.next = after_removal_node.next
47
48 # Clean up: disconnect the removed section (optional but good practice)
49 after_removal_node.next = None
50
51 return list1
52
1/**
2 * Definition for singly-linked list.
3 * public class ListNode {
4 * int val;
5 * ListNode next;
6 * ListNode() {}
7 * ListNode(int val) { this.val = val; }
8 * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
9 * }
10 */
11class Solution {
12 public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) {
13 // Initialize two pointers to traverse list1
14 ListNode nodeBeforeA = list1; // Will point to the node just before position 'a'
15 ListNode nodeAtB = list1; // Will point to the node at position 'b'
16
17 // Move nodeBeforeA to the node just before position 'a' (a-1 steps)
18 // We need to stop one node before 'a' to maintain the connection
19 while (--a > 0) {
20 nodeBeforeA = nodeBeforeA.next;
21 }
22
23 // Move nodeAtB to the node at position 'b' (b steps from start)
24 while (b-- > 0) {
25 nodeAtB = nodeAtB.next;
26 }
27
28 // Connect the node before position 'a' to the head of list2
29 nodeBeforeA.next = list2;
30
31 // Traverse to the end of list2
32 while (nodeBeforeA.next != null) {
33 nodeBeforeA = nodeBeforeA.next;
34 }
35
36 // Connect the tail of list2 to the node after position 'b'
37 nodeBeforeA.next = nodeAtB.next;
38
39 // Clean up: disconnect nodeAtB from the rest of the list (optional but good practice)
40 nodeAtB.next = null;
41
42 // Return the modified list1
43 return list1;
44 }
45}
46
1/**
2 * Definition for singly-linked list.
3 * struct ListNode {
4 * int val;
5 * ListNode *next;
6 * ListNode() : val(0), next(nullptr) {}
7 * ListNode(int x) : val(x), next(nullptr) {}
8 * ListNode(int x, ListNode *next) : val(x), next(next) {}
9 * };
10 */
11class Solution {
12public:
13 ListNode* mergeInBetween(ListNode* list1, int a, int b, ListNode* list2) {
14 // Find the node before position 'a' (where we'll start the merge)
15 ListNode* nodeBeforeA = list1;
16 for (int i = 0; i < a - 1; i++) {
17 nodeBeforeA = nodeBeforeA->next;
18 }
19
20 // Find the node at position 'b' (last node to be removed)
21 ListNode* nodeAtB = list1;
22 for (int i = 0; i <= b; i++) {
23 nodeAtB = nodeAtB->next;
24 }
25
26 // Connect the node before position 'a' to the head of list2
27 nodeBeforeA->next = list2;
28
29 // Traverse to the end of list2
30 ListNode* list2Tail = nodeBeforeA;
31 while (list2Tail->next != nullptr) {
32 list2Tail = list2Tail->next;
33 }
34
35 // Connect the tail of list2 to the node after position 'b'
36 list2Tail->next = nodeAtB;
37
38 return list1;
39 }
40};
41
1/**
2 * Definition for singly-linked list.
3 * class ListNode {
4 * val: number
5 * next: ListNode | null
6 * constructor(val?: number, next?: ListNode | null) {
7 * this.val = (val===undefined ? 0 : val)
8 * this.next = (next===undefined ? null : next)
9 * }
10 * }
11 */
12
13/**
14 * Merges list2 into list1 between nodes at positions a and b.
15 * Removes nodes from position a to position b in list1 and inserts list2 in their place.
16 *
17 * @param list1 - The first linked list
18 * @param a - The starting position (0-indexed) where list2 should be inserted
19 * @param b - The ending position (0-indexed) of nodes to be removed from list1
20 * @param list2 - The second linked list to be inserted
21 * @returns The modified list1 with list2 merged in between
22 */
23function mergeInBetween(
24 list1: ListNode | null,
25 a: number,
26 b: number,
27 list2: ListNode | null,
28): ListNode | null {
29 // Find the node just before position 'a' (where we'll connect list2)
30 let nodeBeforeA: ListNode | null = list1;
31 let nodeAfterB: ListNode | null = list1;
32
33 // Move nodeBeforeA to the node at position (a-1)
34 while (--a > 0) {
35 nodeBeforeA = nodeBeforeA!.next;
36 }
37
38 // Move nodeAfterB to the node at position b
39 while (b-- > 0) {
40 nodeAfterB = nodeAfterB!.next;
41 }
42
43 // Connect the node before position 'a' to the head of list2
44 nodeBeforeA!.next = list2;
45
46 // Traverse to the end of list2
47 while (nodeBeforeA!.next) {
48 nodeBeforeA = nodeBeforeA!.next;
49 }
50
51 // Connect the tail of list2 to the node after position 'b'
52 nodeBeforeA!.next = nodeAfterB!.next;
53
54 // Disconnect the removed portion (optional but good practice for garbage collection)
55 nodeAfterB!.next = null;
56
57 return list1;
58}
59
Time and Space Complexity
Time Complexity: O(m + n)
, where m
is the length of list1
and n
is the length of list2
.
The time complexity breaks down as follows:
- First loop:
O(a)
iterations to position pointerp
at indexa-1
- Second loop:
O(b)
iterations to position pointerq
at indexb
- Third loop:
O(n)
iterations to traverse through all nodes oflist2
to find its tail - Since
a
andb
are at mostm-1
, the worst case for the first two loops isO(m)
- Total:
O(m) + O(n) = O(m + n)
Space Complexity: O(1)
The algorithm only uses a constant amount of extra space:
- Two pointers
p
andq
that reference existing nodes - No additional data structures are created
- The operation modifies the existing linked lists in-place by adjusting pointers
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Off-by-One Error in Pointer Positioning
The Pitfall:
The most common mistake is incorrectly positioning the before_removal_node
pointer. Many developers mistakenly move it a
times instead of a-1
times, which would position it at the a
-th node rather than the node before it.
Incorrect Code:
# WRONG: This moves the pointer TO position a, not BEFORE it
for _ in range(a): # Should be (a-1)
before_removal_node = before_removal_node.next
Why This Fails:
- If you move
a
times,before_removal_node
points to the first node that should be removed - When you then set
before_removal_node.next = list2
, you're keeping one node that should have been removed - This results in an extra node in the final list
The Solution:
Always remember that to insert at position a
, you need to access the node at position a-1
. Use range(a-1)
for the first pointer.
2. Edge Case: When a = 1
(Removing from the Beginning)
The Pitfall:
When a = 1
, the code tries to move the pointer (a-1) = 0
times, which means before_removal_node
stays at the head. This works correctly for the connection but conceptually might be confusing.
Potential Issue: If someone modifies the code without understanding this edge case, they might add special handling that breaks the solution:
# WRONG: Unnecessary special case handling
if a == 1:
# Trying to handle first node removal differently
temp = list1
for _ in range(b):
temp = temp.next
list1 = list2 # This doesn't work! We lose reference to return correct head
The Solution:
Trust the algorithm - when a = 1
:
before_removal_node
stays at the head (moved 0 times)after_removal_node
moves to positionb
- Setting
before_removal_node.next = list2
correctly removes nodes 1 through b - The original head is preserved and returned correctly
3. Not Handling Empty list2
The Pitfall:
If list2
is empty (null), the traversal loop while before_removal_node.next:
won't execute, and before_removal_node
remains pointing to the node before position a
.
Problematic Scenario:
# If list2 is None or empty:
before_removal_node.next = list2 # Sets to None
while before_removal_node.next: # Loop doesn't execute
before_removal_node = before_removal_node.next
before_removal_node.next = after_removal_node.next # Still works correctly!
Why It Actually Works:
The code handles this gracefully because when list2
is None
, the connection still works properly - we're essentially just removing nodes without inserting anything.
Potential Enhancement (if needed):
if list2: # Only traverse if list2 exists
before_removal_node.next = list2
while before_removal_node.next:
before_removal_node = before_removal_node.next
else:
# Direct connection when list2 is empty
pass # The next line handles it
before_removal_node.next = after_removal_node.next
4. Confusion with 0-indexed vs 1-indexed Positions
The Pitfall:
The problem statement uses 1-indexed positions (nodes are counted starting from 1), but the implementation treats a
and b
as if they follow this convention. Some developers might get confused and try to adjust for 0-indexing.
Wrong Adjustment:
# WRONG: Don't adjust if the problem already uses 1-indexing
for _ in range(a - 2): # Incorrectly subtracting 2
before_removal_node = before_removal_node.next
The Solution: Carefully read whether the problem uses 0-indexing or 1-indexing. In this case:
- Position 1 = first node
- Position 2 = second node
- To get to the node before position
a
, move(a-1)
times from the head
Which of the following shows the order of node visit in a Breadth-first Search?
Recommended Readings
Linked List Cycle Given a linked list with potentially a loop determine whether the linked list from the first node contains a cycle in it For bonus points do this with constant space Parameters nodes The first node of a linked list with potentially a loop Result Whether there is a loop contained
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!