Merging Multiple Linked Lists into a Single Linked List

Merging multiple linked lists involves combining two or more linked lists into a single, consolidated linked list. This operation is crucial for efficiently managing and manipulating large data which requires frequent regrouping or reordering. One example use case is in text editors, where text is often represented as a collection of linked lists (e.g., one list per line or paragraph) and these lists are merged when converting the document to plain text or when performing operations like search and replace across the entire document.

Note that the merging we discuss here is different from merging of sorted linked lists. In our current problem, the merged linked list need not be in sorted order.

Prerequisite for this article: Basics of Linked List Data Structure.

Problem Statement

The problem of merging multiple linked lists involves combining two or more separate linked lists into a single, unified linked list. This operation should preserve the original order of elements within each list while ensuring that all the nodes from all the linked lists are connected together.

Examples

Input-1:

  • List 1: 1 -> 3 -> 5
  • List 2: 2 -> 4 -> 6
  • List 3: 7 -> 8 -> 9

Expected Output-1:

  • 1 -> 3 -> 5 -> 2 -> 4 -> 6 -> 7 -> 8 -> 9

Input-2:

  • List 1: A -> C -> E
  • List 2: B -> D -> F

Expected Output-2:

  • A -> C -> E -> B -> D -> F

Constraints and Assumptions

  • We assume that we only have access to the head node of each list, not the tail node.
  • The merging process does not involve sorting the elements. The goal is to just combine the lists while maintaining the original order in each list.
  • The linked lists may have different lengths.
  • There are no loops within the input lists.
  • Memory efficiency should be considered, avoiding unnecessary copying of nodes when possible.

Different Approaches

Sequential Approach

Below is the algorithm for merging multiple linked lists using sequential approach:

  1. Start with the first list as the base for the merged list.
  2. Initialize a current pointer to the head of the merged list.
  3. For each remaining list:
    • Traverse the merged list using the ‘current’ pointer until reaching its tail (i.e., until current.next is null).
    • Connect this tail to the head of the next list by setting current.next to the head of the next list.
    • Update current to point to the head of the newly added list.
  4. Repeat steps 3 until all lists are merged.

# Definition for singly-linked list node
class Node:
  def __init__(self, data):
    self.data = data
    self.next = None

# Function to merge k linked lists
def merge_k_lists(lists):
  # Check if the input list is empty
  if not lists:
    return None
  
  # Start with the first list as the base for the merged list
  merged = lists[0]
  
  # Iterate through the remaining lists
  for i in range(1, len(lists)):
    # If the current list is empty, continue to the next one
    if not lists[i]:
      continue
    
    # If the merged list is empty, set it to the current list
    if not merged:
      merged = lists[i]
      continue
    
    # Initialize the current pointer to the head of the merged list
    current = merged
    
    # Traverse to the end of the merged list
    while current.next:
      current = current.next
    
    # Connect the tail of the merged list to the head of the next list
    current.next = lists[i]
  
  return merged

# Helper function to create a linked list from a Python list
def create_linked_list(arr):
  if not arr:
    return None
  head = Node(arr[0])
  current = head
  for val in arr[1:]:
    current.next = Node(val)
    current = current.next
  return head

# Helper function to print a linked list
def print_linked_list(head):
  current = head
  while current:
    print(current.data, end=" -> ")
    current = current.next
  print("None")

# Example usage
list1 = create_linked_list([1, 4, 5])
list2 = create_linked_list([1, 3, 4])
list3 = create_linked_list([2, 6])

lists = [list1, list2, list3]
merged = merge_k_lists(lists)

print("Merged list:")
print_linked_list(merged)

Time and Space Complexity:

  • Time Complexity: O(n), where n is the total number of nodes across all lists.
  • Space Complexity: O(1), as we only use a constant amount of extra space.

Advantages and Disadvantages:

  • Advantages:
    • Simple to implement
    • Memory efficient
  • Disadvantages:
    • May not be optimal for a large number of lists

Recursive Approach

The recursive approach to merging multiple linked lists involves recursively combining pairs of lists until all lists are merged. Here’s the algorithm:

  1. If there’s only one list or no lists, return that list or null respectively.
  2. If there are two or more lists:
    • Recursively merge the first half of the lists.
    • Recursively merge the second half of the lists.
    • Merge the two resulting lists.
  3. To merge two lists:
    • If one list is empty, return the other list.
    • Otherwise, connect the tail of first list with the head node of second list.

# Definition for singly-linked list node
class Node:
  def __init__(self, data):
    self.data = data
    self.next = None

# Function to merge k linked lists recursively
def merge_k_lists(lists):
  # Base case: if the list is empty, return None
  if not lists:
    return None
  
  # Base case: if there's only one list, return it
  if len(lists) == 1:
    return lists[0]
  
  # Divide the lists into two halves
  mid = len(lists) // 2
  left = merge_k_lists(lists[:mid])
  right = merge_k_lists(lists[mid:])
  
  # Merge the two halves
  return merge_two_lists(left, right)

# Helper function to merge two linked lists
def merge_two_lists(l1, l2):
  # Base cases: if one list is empty, return the other
  if not l1:
    return l2
  if not l2:
    return l1
  
  # Find tail node of list1 and connect it to head of list2
  current = l1
  while current.next:
    current = current.next
  current.next = l2
  
  return l1

# Helper function to create a linked list from a Python list
def create_linked_list(arr):
  if not arr:
    return None
  head = Node(arr[0])
  current = head
  for val in arr[1:]:
    current.next = Node(val)
    current = current.next
  return head

# Helper function to print a linked list
def print_linked_list(head):
  current = head
  while current:
    print(current.data, end=" -> ")
    current = current.next
  print("None")

# Example usage
list1 = create_linked_list([1, 4, 5])
list2 = create_linked_list([1, 3, 4])
list3 = create_linked_list([2, 6])

lists = [list1, list2, list3]
merged = merge_k_lists(lists)

print("Merged list:")
print_linked_list(merged)

Time and Space Complexity:

  • Time Complexity: O(n), where n is the total number of nodes.
  • Space Complexity: O(log k) due to the recursive call stack where k is the total number of lists.

Advantages and Disadvantages:

  • Advantages:
    • Elegant and concise implementation
    • Potential for parallelization/multithreading
  • Disadvantages:
    • Higher space complexity due to recursive calls
    • May cause stack overflow for very large number of lists

Similar Extensions of this Problem

  • Merge 2 Sorted Linked Lists: This problem involves combining two sorted linked lists into a single sorted linked list.
  • Merge K Sorted Lists: This problem involves merging K sorted linked lists into a single sorted linked list. It’s a more complex version of the basic merge problem as it requires maintaining sorted order during the merge.
  • Zip of Linked Lists: This involves interleaving nodes from multiple lists to create a single list. For example, given lists A->B->C and 1->2->3, the result would be A->1->B->2->C->3.
  • Merge with Deduplication: This extension involves merging multiple lists while removing duplicate elements, which adds an extra layer of complexity to the basic merging process.
  • Merge and Group: This involves merging multiple linked lists into multiple other linked lists based on certain rules, useful for grouping or categorizing data. For example, merging employee lists from different departments into lists based on job roles or salary ranges etc.