合并 K 个排序链表 (2)

84 阅读7分钟

合并 K 个排序链表

给定 K 个大小为 N 的已排序链表,任务是将它们全部合并并保持其排序顺序。

例子:

输入:K = 3,N = 4
list1 = 1->3->5->7->NULL
list2 = 2->4->6->8->NULL
list3 = 0->9->10->11->NULL
输出:0->1->2->3->4->5->6->7->8->9->10->11
按排序顺序合并列表,其中每个元素都大于等于前一个元素。

输入:K = 3,N = 3
list1 = 1->3->7->NULL
list2 = 2->4->8->NULL
list3 = 9->10->11->NULL
输出:1->2->3->4->7->8->9->10->11
按排序顺序合并列表,其中每个元素都大于等于前一个元素。

书接上回 合并 k 个排序链表 (1)

使用最小堆合并 K 个排序链表

请按照给定的步骤解决问题:

  • 创建一个最小堆并插入所有链表的第一个元素。
  • 只要min-heap不为空,就执行以下步骤:
    • 删除最小堆的根(这是最小堆中所有元素中当前的最小值)并将其添加到结果列表中。
    • 如果上一步弹出的元素后边存在一个元素(在同一个链表中),则将其插入到最小堆中。
  • 返回合并链表的头节点地址。

下面是上述方法的实现:

C++

// C++ implementation to merge k
// sorted linked lists
// Using MIN HEAP method
 
#include <bits/stdc++.h>
using namespace std;
 
struct Node 
{
    int data;
    struct Node* next;
};
 
// Utility function to create 
// a new node
struct Node* newNode(int data)
{
    // Allocate node
    struct Node* new_node = new Node();
 
    // Put in the data
    new_node->data = data;
    new_node->next = NULL;
 
    return new_node;
}
 
// 'compare' function used to build 
// up the priority queue
struct compare 
{
    bool operator()(
         struct Node* a, struct Node* b)
    {
        return a->data > b->data;
    }
};
 
// Function to merge k sorted linked lists
struct Node* mergeKSortedLists(
             struct Node* arr[], int K)
{
    // Priority_queue 'pq' implemented
    // as min heap with the help of 
    // 'compare' function
    priority_queue<Node*, vector<Node*>, compare> pq;
 
    // Push the head nodes of all
    // the k lists in 'pq'
    for (int i = 0; i < K; i++)
        if (arr[i] != NULL)
            pq.push(arr[i]);
     
      // Handles the case when k = 0 
      // or lists have no elements in them    
      if (pq.empty())
        return NULL;
   
      struct Node *dummy = newNode(0);
      struct Node *last = dummy;
   
    // Loop till 'pq' is not empty
    while (!pq.empty())  
    {
        // Get the top element of 'pq'
        struct Node* curr = pq.top();
        pq.pop();
 
        // Add the top element of 'pq'
        // to the resultant merged list
        last->next = curr;
        last = last->next;  
       
        // Check if there is a node
         // next to the 'top' node
        // in the list of which 'top'
        // node is a member
        if (curr->next != NULL)
             
        // Push the next node of top node 
        // in 'pq'
        pq.push(curr->next);
    }
 
    // Address of head node of the required 
    // merged list
    return dummy->next;
}
 
// Function to print the singly 
// linked list
void printList(struct Node* head)
{
    while (head != NULL) 
    {
        cout << head->data << " ";
        head = head->next;
    }
}
 
// Driver code
int main()
{
    // Number of linked lists
    int N = 3; 
 
    // Number of elements in each list
    int K = 4; 
 
    // An array of pointers storing the 
    // head nodes of the linked lists
    Node* arr[N];
 
    // Creating k = 3 sorted lists
    arr[0] = newNode(1);
    arr[0]->next = newNode(3);
    arr[0]->next->next = newNode(5);
    arr[0]->next->next->next = newNode(7);
 
    arr[1] = newNode(2);
    arr[1]->next = newNode(4);
    arr[1]->next->next = newNode(6);
    arr[1]->next->next->next = newNode(8);
 
    arr[2] = newNode(0);
    arr[2]->next = newNode(9);
    arr[2]->next->next = newNode(10);
    arr[2]->next->next->next = newNode(11);
 
    // Merge the k sorted lists
    struct Node* head = mergeKSortedLists(arr, N);
 
    // Print the merged list
    printList(head);
 
    return 0;
}

Java

// Java code for the above approach
 
import java.io.*;
import java.util.*;
 
class Node {
    int data;
    Node next;
 
    Node(int key)
    {
        data = key;
        next = null;
    }
}
 
// Class implements Comparator to compare Node data
class NodeComparator implements Comparator<Node> {
 
    public int compare(Node k1, Node k2)
    {
        if (k1.data > k2.data)
            return 1;
        else if (k1.data < k2.data)
            return -1;
        return 0;
    }
}
class GFG {
    // Function to merge k sorted linked lists
    static Node mergeKList(Node[] arr, int K)
    {
        // Priority_queue 'queue' implemented
        // as min heap with the help of
        // 'compare' function
        PriorityQueue<Node> queue
            = new PriorityQueue<>(new NodeComparator());
        Node at[] = new Node[K];
        Node head = new Node(0);
        Node last = head;
        // Push the head nodes of all
        // the k lists in 'queue'
        for (int i = 0; i < K; i++) {
            if (arr[i] != null) {
                queue.add(arr[i]);
            }
        }
        // Handles the case when k = 0
        // or lists have no elements in them
        if (queue.isEmpty())
            return null;
        // Loop till 'queue' is not empty
        while (!queue.isEmpty()) {
            // Get the top element of 'queue'
            Node curr = queue.poll();
 
            // Add the top element of 'queue'
            // to the resultant merged list
            last.next = curr;
            last = last.next;
            // Check if there is a node
            // next to the 'top' node
            // in the list of which 'top'
            // node is a member
            if (curr.next != null) {
                // Push the next node of top node
                // in 'queue'
                queue.add(curr.next);
            }
        }
        // Address of head node of the required
        // merged list
        return head.next;
    }
    // Print linked list
    public static void printList(Node node)
    {
        while (node != null) {
            System.out.print(node.data + " ");
            node = node.next;
        }
    }
 
    public static void main(String[] args)
    {
        int N = 3;
       
        // array to store head of linkedlist
        Node[] a = new Node[N];
       
        // Linkedlist1
        Node head1 = new Node(1);
        a[0] = head1;
        head1.next = new Node(3);
        head1.next.next = new Node(5);
          head1.next.next.next = new Node(7);
       
        // Limkedlist2
        Node head2 = new Node(2);
        a[1] = head2;
        head2.next = new Node(4);
          head2.next.next = new Node(6);
          head2.next.next.next = new Node(8);
       
        // Linkedlist3
        Node head3 = new Node(0);
        a[2] = head3;
        head3.next = new Node(9);
          head3.next.next = new Node(10);
          head3.next.next.next = new Node(11);
 
        Node res = mergeKList(a, N);
 
        if (res != null)
            printList(res);
        System.out.println();
    }
}

Python

# Python implementation to merge k sorted linked lists
# Using MIN HEAP method
 
import heapq
 
class Node:
    def __init__(self, data):
        self.data = data
        self.next = None
 
def mergeKList(arr, K):
    # Priority_queue 'queue' implemented
    # as min heap with the help of
    # 'compare' function
    queue = []
    for i in range(K):
        if arr[i] != None:
            heapq.heappush(queue, (arr[i].data, arr[i]))
    dummy = Node(0)
    last = dummy
    while queue:
        curr = heapq.heappop(queue)[1]
        last.next = curr
        last = last.next
        if curr.next != None:
            heapq.heappush(queue, (curr.next.data, curr.next))
    return dummy.next
 
def printList(node):
    while node != None:
        print(node.data, end=" ")
        node = node.next
    print()
 
if __name__ == "__main__":
    N = 3
    a = [None] * N
    # Linkedlist1
    head1 = Node(1)
    a[0] = head1
    head1.next = Node(3)
    head1.next.next = Node(5)
    head1.next.next.next = Node(7)
    # Limkedlist2
    head2 = Node(2)
    a[1] = head2
    head2.next = Node(4)
    head2.next.next = Node(6)
    head2.next.next.next = Node(8)
    # Linkedlist3
    head3 = Node(0)
    a[2] = head3
    head3.next = Node(9)
    head3.next.next = Node(10)
    head3.next.next.next = Node(11)
    res = mergeKList(a, N)
    if res != None:
        printList(res)
 
 

C#

using System;
using System.Collections.Generic;
 
class Node
{
    public int data;
    public Node next;
 
    public Node(int key)
    {
        data = key;
        next = null;
    }
}
 
// Custom comparer to compare nodes based on data
class NodeComparer : IComparer<Node>
{
    public int Compare(Node k1, Node k2)
    {
        if (k1.data > k2.data)
            return 1;
        else if (k1.data < k2.data)
            return -1;
        return 0;
    }
}
 
class GFG
{
    // Function to merge k sorted linked lists
    static Node MergeKList(Node[] arr, int K)
    {
        // Priority queue implemented as a min-heap using custom comparer
        PriorityQueue<Node> queue = new PriorityQueue<Node>(new NodeComparer());
        Node head = new Node(0);
        Node last = head;
 
        // Enqueue the head nodes of all k lists in the queue
        for (int i = 0; i < K; i++)
        {
            if (arr[i] != null)
            {
                queue.Enqueue(arr[i]);
            }
        }
 
        // If the queue is empty, return null
        if (queue.Count == 0)
            return null;
 
        // Loop until the queue is not empty
        while (queue.Count > 0)
        {
            // Get the node with the smallest data from the queue
            Node curr = queue.Dequeue();
 
            // Add the node to the resultant merged list
            last.next = curr;
            last = last.next;
 
            // Check if there is a node next to the current node in its list
            if (curr.next != null)
            {
                // Enqueue the next node in the list
                queue.Enqueue(curr.next);
            }
        }
 
        // Return the head of the merged list
        return head.next;
    }
 
    // Print linked list
    public static void PrintList(Node node)
    {
        while (node != null)
        {
            Console.Write(node.data + " ");
            node = node.next;
        }
    }
 
    static void Main(string[] args)
    {
        int N = 3;
 
        Node[] a = new Node[N];
 
        // Linked list 1
        Node head1 = new Node(1);
        a[0] = head1;
        head1.next = new Node(3);
        head1.next.next = new Node(5);
        head1.next.next.next = new Node(7);
 
        // Linked list 2
        Node head2 = new Node(2);
        a[1] = head2;
        head2.next = new Node(4);
        head2.next.next = new Node(6);
        head2.next.next.next = new Node(8);
 
        // Linked list 3
        Node head3 = new Node(0);
        a[2] = head3;
        head3.next = new Node(9);
        head3.next.next = new Node(10);
        head3.next.next.next = new Node(11);
 
        Node res = MergeKList(a, N);
 
        if (res != null)
            PrintList(res);
 
        Console.WriteLine();
    }
}
 
// Priority queue implementation
class PriorityQueue<T>
{
    private List<T> heap;
    private IComparer<T> comparer;
 
    public PriorityQueue(IComparer<T> comparer = null)
    {
        this.heap = new List<T>();
        this.comparer = comparer ?? Comparer<T>.Default;
    }
 
    public int Count => heap.Count;
 
    // Enqueue operation
    public void Enqueue(T item)
    {
        heap.Add(item);
        int i = heap.Count - 1;
        while (i > 0)
        {
            int parent = (i - 1) / 2;
            if (comparer.Compare(heap[parent], heap[i]) <= 0)
                break;
            Swap(parent, i);
            i = parent;
        }
    }
 
    // Dequeue operation
    public T Dequeue()
    {
        if (heap.Count == 0)
            throw new InvalidOperationException("Priority queue is empty.");
        T result = heap[0];
        int last = heap.Count - 1;
        heap[0] = heap[last];
        heap.RemoveAt(last);
        last--;
        int i = 0;
        while (true)
        {
            int left = 2 * i + 1;
            if (left > last)
                break;
            int right = left + 1;
            int minChild = left;
            if (right <= last && comparer.Compare(heap[right], heap[left]) < 0)
                minChild = right;
            if (comparer.Compare(heap[i], heap[minChild]) <= 0)
                break;
            Swap(i, minChild);
            i = minChild;
        }
        return result;
    }
 
    // Swap two elements in the heap
    private void Swap(int i, int j)
    {
        T temp = heap[i];
        heap[i] = heap[j];
        heap[j] = temp;
    }
}

Javascript

class Node {
  constructor(key) {
    this.data = key;
    this.next = null;
  }
}
 
// Comparator function to compare Node data
function nodeComparator(k1, k2) {
  if (k1.data < k2.data) return -1;
  if (k1.data > k2.data) return 1;
  return 0;
}
 
class PriorityQueue {
  constructor(compare) {
    this.heap = [];
    this.compare = compare;
  }
 
  enqueue(value) {
    this.heap.push(value);
    this.bubbleUp();
  }
 
  bubbleUp() {
    let index = this.heap.length - 1;
    while (index > 0) {
      let element = this.heap[index],
        parentIndex = Math.floor((index - 1) / 2),
        parent = this.heap[parentIndex];
      if (this.compare(element, parent) < 0) break;
      this.heap[index] = parent;
      this.heap[parentIndex] = element;
      index = parentIndex;
    }
  }
 
  dequeue() {
    let max = this.heap[0];
    let end = this.heap.pop();
    if (this.heap.length > 0) {
      this.heap[0] = end;
      this.sinkDown(0);
    }
    return max;
  }
 
  sinkDown(index) {
    let left = 2 * index + 1,
      right = 2 * index + 2,
      largest = index;
 
    if (
      left < this.heap.length &&
      this.compare(this.heap[left], this.heap[largest]) > 0
    ) {
      largest = left;
    }
 
    if (
      right < this.heap.length &&
      this.compare(this.heap[right], this.heap[largest]) > 0
    ) {
      largest = right;
    }
 
    if (largest !== index) {
      [this.heap[largest], this.heap[index]] = [
        this.heap[index],
        this.heap[largest],
      ];
      this.sinkDown(largest);
    }
  }
 
  isEmpty() {
    return this.heap.length === 0;
  }
}
 
function mergeKLists(arr, K) {
  const queue = new PriorityQueue(nodeComparator);
  const at = new Array(K);
  const head = new Node(0);
  let last = head;
 
  // Push the head nodes of all the k lists in 'queue'
  for (let i = 0; i < K; i++) {
    if (arr[i] !== null) {
      queue.enqueue(arr[i]);
    }
  }
 
  // Handles the case when k = 0
  // or lists have no elements in them
  if (queue.isEmpty()) return null;
 
  // Loop till 'queue' is not empty
  while (!queue.isEmpty()) {
    // Get the top element of 'queue'
    const curr = queue.dequeue();
 
    // Add the top element of 'queue' to the resultant merged list
    last.next = curr;
    last = last.next;
 
    // Check if there is a node next to the 'top' node
    // in the list of which 'top' node is a member
    if (curr.next !== null) {
      // Push the next node of top node in 'queue'
      queue.enqueue(curr.next);
    }
  }
 
  // Address of head node of the required merged list
  return head.next;
}
 
// Print linked list
function printList(node) {
  let str = "";
  while (node !== null) {
    str += `${node.data} `;
    node = node.next;
  }
  console.log(str);
}
 
// Testing
const N = 3;
 
// array to store head of linkedlist
const a = new Array(N);
 
// Linkedlist1
const head1 = new Node(1);
a[0] = head1;
head1.next = new Node(3);
head1.next.next = new Node(5);
head1.next.next.next = new Node(7);
 
// Limkedlist2
const head2 = new Node(2);
a[1] = head2;
head2.next = new Node(4);
head2.next.next = new Node(6);
head2.next.next.next = new Node(8);
 
// Linkedlist3
const head3 = new Node(0);
a[2] = head3;
head3.next = new Node(9);
head3.next.next = new Node(10);
head3.next.next.next = new Node(11);
 
const res = mergeKLists(a, N);
 
if (res !== null) printList(res);
console.log();

输出

0 1 2 3 4 5 6 7 8 9 10 11 

时间复杂度:O(N * K * log K)
辅助空间:O(K)