Merge In Between Linked Lists

Updated on 16 June, 2025
Merge In Between Linked Lists header image

Problem Statement

In this task, you have two singly linked lists, list1 and list2, where the former has size n and the latter size m. The goal is to modify list1 by removing a certain segment of its nodes, specifically from the ath node to the bth node (both inclusive), and then insert all the nodes of list2 into list1 at the position where the first removed node was. The process transforms list1 by combining parts of it with all of list2. Post operation, the method should return the head of the updated linked list. The problem asks to ensure this merged linked list reflects the desired sequence of nodes as specified by the indices a and b, and the structure of list2.

Examples

Example 1

Input:

list1 = [10,1,13,6,9,5], a = 3, b = 4, list2 = [1000000,1000001,1000002]

Output:

[10,1,13,1000000,1000001,1000002,5]

Explanation:

We remove the nodes 3 and 4 and put the entire list2 in their place. The blue edges and nodes in the above figure indicate the result.

Example 2

Input:

list1 = [0,1,2,3,4,5,6], a = 2, b = 5, list2 = [1000000,1000001,1000002,1000003,1000004]

Output:

[0,1,1000000,1000001,1000002,1000003,1000004,6]

Explanation:

The blue edges and nodes in the above figure indicate the result.

Constraints

  • 3 <= list1.length <= 104
  • 1 <= a <= b < list1.length - 1
  • 1 <= list2.length <= 104

Approach and Intuition

To achieve the merging of list2 into list1 at specified positions, follow these systematic steps:

  1. Identify the nodes just before and just after the segment [a, b] in list1. Let's call these pre_a and post_b.
  2. Traverse list1 to find pre_a (the node right before a). If a is 1, pre_a will be a new dummy head.
  3. Similarly, find post_b (the node right after b).
  4. The next step involves disconnecting the segment from a to b:
    • Set the next pointer of pre_a (if it exists) to the head of list2.
  5. Traverse to the end of list2 to connect list2’s last node to post_b. This action effectively inserts list2.
  6. If a equals 1, update the head of the list to be the head of list2.

Taking into account the constraints ensures that the values for a and b are within valid ranges, and that both linked lists are of permissible lengths. Maintaining the integrity of the linked list while making the insertions and deletions is crucial for the correct manipulation of node pointers. Make sure to appropriately handle cases where list2 might be empty or exceptionally large compared to list1. Also, consider edge cases such as inserting at the very beginning or the end of the list. This approach combines traversal, pointer manipulation, and requires careful attention to node connections for achieving the desired linked list transformations.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
public:
    ListNode* integrateNodes(ListNode* list1, int startIdx, int endIdx, ListNode* list2) {
        return integrate(list1, startIdx, endIdx, list2, 0, nullptr, list1);
    }
private:
    ListNode* integrate(ListNode* list1, int startIdx, int endIdx, ListNode* list2, 
            int currentIndex, ListNode* lastNode, ListNode* currentNode) {
        if (currentIndex == startIdx - 1) {
            lastNode = currentNode;
        }

        if (currentIndex == endIdx) {
            lastNode->next = list2;
            ListNode* tail = getTail(list2);
            tail->next = currentNode->next;
            currentNode->next = nullptr;
            return list1;
        }

        return integrate(list1, startIdx, endIdx, list2, currentIndex + 1, lastNode, currentNode->next);
    }

    ListNode* getTail(ListNode* node) {
        while (node->next != nullptr) {
            node = node->next;
        }
        return node;
    }       
};

In the given C++ code, the task is to merge two singly linked lists such that one list is inserted into another between two specific indices. The code defined inside the Solution class features a public member function integrateNodes and a private member function integrate.

Code Explanation:

  • The integrateNodes function serves as the main entry point. It calls the integrate function, beginning the merging process by passing the initial linked list (list1), the indices between which list2 should be integrated (startIdx and endIdx), and the head of the second linked list (list2).

  • The integrate function recurses through the nodes of list1. It uses the parameters:

    • currentIndex to keep track of the current node index in list1.
    • lastNode to remember the node just before the startIdx.
    • currentNode as the current node in the traversal of list1.
  • When currentIndex equals startIdx - 1, the function updates lastNode to currentNode, marking the node after which list2 should be linked.

  • As currentIndex reaches endIdx, the function performs the splice:

    • lastNode->next is updated to point to list2,
    • tail (retrieved using the helper function getTail) is the last node of list2 after which the function connects the part of list1 that follows endIdx.
  • The helper function getTail iterates through list2 to find its tail node.

**Executing this code will result in list1 being modified in place. The section from startIdx to endIdx of list1 is replaced by all nodes in list2. Following the integration, nodes from endIdx + 1 onward in list1 will continue after the nodes from list2. This approach uses recursion to efficiently manage the index counting and node traversal, ensuring a seamless integration without managing multiple loops or additional data structures.

java
class Solution {
    public ListNode mergeTwo(ListNode list1, int startIdx, int endIdx, ListNode list2) {
        // Initiate merge helper with initial values
        return mergeHelper(list1, startIdx, endIdx, list2, 0, null, list1);
    }

    private ListNode mergeHelper(ListNode list1, int startIdx, int endIdx, ListNode list2,
            int currentIdx, ListNode prevNode, ListNode currentNode) {
        // Set prevNode at the node before startIdx
        if (currentIdx == startIdx - 1) {
            prevNode = currentNode;
        }

        // Recursive base case
        if (currentIdx == endIdx) {
            // Connect prevNode to list2's head
            prevNode.next = list2;
            // Search for the last node of list2
            ListNode tail = getTail(list2);
            tail.next = currentNode.next;
            currentNode.next = null;
            return list1;
        }

        return mergeHelper(list1, startIdx, endIdx, list2, currentIdx + 1, prevNode, currentNode.next);
    }

    private ListNode getTail(ListNode node) {
        // Traverse to the end of the list
        if (node.next == null) {
            return node;
        }
        return getTail(node.next);
    }    
}

The given Java solution involves merging one linked list into another between specified positions. The code defines a Solution class with the method mergeTwo, which integrates list2 into list1 starting just after the node at index startIdx and ending at the node at index endIdx. The integration is done using a helper method, mergeHelper, that uses recursion to traverse list1 and insert list2.

  • The method mergeTwo calls mergeHelper with initial values representing the current state of the traversal.
  • mergeHelper traverses list1 recursively. At startIdx - 1, it records the node just before where list2 should be inserted.
  • When the traversal reaches endIdx, it links the last node before startIdx (prevNode) to the head of list2.
  • The method then continues to find the tail of list2 using another helper method, getTail, which recursively reaches the last node of list2.
  • This tail node is then connected to the node following endIdx in list1, thus linking list2 correctly in between list1.
  • Finally, by connecting the nodes and handling the recursion base cases properly, the method ensures list1 is updated and returned with list2 merged in between the specified indices.

To summarize, this implementation effectively merges two linked lists by manipulating node connections and utilizes recursion for node traversal and merging, making sure the list integrity is maintained throughout the process. Ensure to verify edge cases, like overlapping indices or null lists, for a robust application.

python
class Solution:
    def spliceLists(self, first_list: ListNode, left: int, right: int, second_list: ListNode) -> ListNode:
        def get_tail(node: ListNode) -> ListNode:
            if node.next is None:
                return node
            return get_tail(node.next)

        def integrate(index, precursor, terminal):
            if index == left - 1:
                precursor = terminal

            if index == right:
                precursor.next = second_list
                list2_tail = get_tail(second_list)
                list2_tail.next = terminal.next
                terminal.next = None
                return first_list

            return integrate(index + 1, precursor, terminal.next)

        return integrate(0, None, first_list)

The provided Python code defines a class Solution with a method spliceLists that merges one linked list into another at specified positions. The method takes four parameters: first_list, left, right, and second_list. first_list is the primary linked list where second_list will be inserted between the indices left and right.

The method uses two helper functions:

  • get_tail: Recurses through the list to find and return the tail (the last node) of the list.
  • integrate: Recursive function handling navigation of the first_list and splicing in second_list. It adjusts pointers to merge second_list into first_list between nodes at positions left and right.

Steps in the process:

  1. Call integrate with initial parameters, starting from the head of first_list.
  2. Traverse first_list to find the node just before position left (pre-modification point).
  3. Attach the start of second_list to this node.
  4. Find the tail of second_list using get_tail.
  5. Attach the node following position right in first_list to the tail of second_list, effectively inserting second_list between left and right.
  6. Return the head of the modified first_list.

The approach ensures that the insertion of second list is seamless, controlled through recursive traversal and pointer reassignment to maintain list integrity. Handling edge cases, like list bounds, would be implicit in the logic through careful index management and null handling. Ensuring efficient traversal and proper tail connection is crucial for maintaining linked list structure.

Comments

No comments yet.