diff --git a/DIRECTORY.md b/DIRECTORY.md index 95805ff41..820b80cde 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -830,6 +830,7 @@ * lists * [CircleLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/CircleLinkedListTest.java) * [CreateAndDetectLoopTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoopTest.java) + * [MergeKSortedLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedListTest.java) * [MergeSortedArrayListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedArrayListTest.java) * [MergeSortedSinglyLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedSinglyLinkedListTest.java) * [QuickSortLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/QuickSortLinkedListTest.java) diff --git a/src/main/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedList.java b/src/main/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedList.java index 0eac20d2e..0f5c50530 100644 --- a/src/main/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedList.java +++ b/src/main/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedList.java @@ -1,51 +1,94 @@ package com.thealgorithms.datastructures.lists; -import java.util.Arrays; import java.util.Comparator; import java.util.PriorityQueue; /** + * The MergeKSortedLinkedList class provides a method to merge multiple sorted linked lists + * into a single sorted linked list. + * This implementation uses a min-heap (priority queue) to efficiently + * find the smallest node across all lists, thus optimizing the merge process. + * + *

Example usage: + *

+ * Node list1 = new Node(1, new Node(4, new Node(5)));
+ * Node list2 = new Node(1, new Node(3, new Node(4)));
+ * Node list3 = new Node(2, new Node(6));
+ * Node[] lists = { list1, list2, list3 };
+ *
+ * MergeKSortedLinkedList merger = new MergeKSortedLinkedList();
+ * Node mergedHead = merger.mergeKList(lists, lists.length);
+ * 
+ *

+ * + *

This class is designed to handle nodes of integer linked lists and can be expanded for additional data types if needed.

+ * * @author Arun Pandey (https://github.com/pandeyarun709) */ public class MergeKSortedLinkedList { /** - * This function merge K sorted LinkedList + * Merges K sorted linked lists into a single sorted linked list. * - * @param a array of LinkedList - * @param n size of array - * @return node + *

This method uses a priority queue (min-heap) to repeatedly extract the smallest node from the heads of all the lists, + * then inserts the next node from that list into the heap. The process continues until all nodes have been processed, + * resulting in a fully merged and sorted linked list.

+ * + * @param a Array of linked list heads to be merged. + * @param n Number of linked lists. + * @return Head of the merged sorted linked list. */ Node mergeKList(Node[] a, int n) { - // Min Heap - PriorityQueue min = new PriorityQueue<>(Comparator.comparingInt(x -> x.data)); + if (a == null || n == 0) { + return null; + } - // adding head of all linkedList in min heap - min.addAll(Arrays.asList(a).subList(0, n)); + // Min Heap to store nodes based on their values for efficient retrieval of the smallest element. + PriorityQueue minHeap = new PriorityQueue<>(Comparator.comparingInt(x -> x.data)); + + // Initialize the min-heap with the head of each non-null linked list + for (Node node : a) { + if (node != null) { + minHeap.add(node); + } + } + + // Start merging process + Node head = minHeap.poll(); // Smallest head is the initial head of the merged list + if (head != null && head.next != null) { + minHeap.add(head.next); + } - // Make new head among smallest heads in K linkedList - Node head = min.poll(); - min.add(head.next); Node curr = head; - - // merging LinkedList - while (!min.isEmpty()) { - Node temp = min.poll(); + while (!minHeap.isEmpty()) { + Node temp = minHeap.poll(); curr.next = temp; curr = temp; - // Add Node in min Heap only if temp.next is not null + // Add the next node in the current list to the heap if it exists if (temp.next != null) { - min.add(temp.next); + minHeap.add(temp.next); } } return head; } - private final class Node { + /** + * Represents a node in the linked list. + */ + static class Node { + int data; + Node next; - private int data; - private Node next; + Node(int data) { + this.data = data; + this.next = null; + } + + Node(int data, Node next) { + this.data = data; + this.next = next; + } } } diff --git a/src/test/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedListTest.java b/src/test/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedListTest.java new file mode 100644 index 000000000..99a890112 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedListTest.java @@ -0,0 +1,93 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import com.thealgorithms.datastructures.lists.MergeKSortedLinkedList.Node; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +class MergeKSortedLinkedListTest { + + @Test + void testMergeKLists() { + Node list1 = new Node(1, new Node(4, new Node(5))); + Node list2 = new Node(1, new Node(3, new Node(4))); + Node list3 = new Node(2, new Node(6)); + Node[] lists = {list1, list2, list3}; + + MergeKSortedLinkedList merger = new MergeKSortedLinkedList(); + Node mergedHead = merger.mergeKList(lists, lists.length); + + int[] expectedValues = {1, 1, 2, 3, 4, 4, 5, 6}; + int[] actualValues = getListValues(mergedHead); + assertArrayEquals(expectedValues, actualValues, "Merged list values do not match the expected sorted order."); + } + + @Test + void testMergeEmptyLists() { + Node[] lists = {null, null, null}; + + MergeKSortedLinkedList merger = new MergeKSortedLinkedList(); + Node mergedHead = merger.mergeKList(lists, lists.length); + + assertNull(mergedHead, "Merged list should be null when all input lists are empty."); + } + + @Test + void testMergeSingleList() { + Node list1 = new Node(1, new Node(3, new Node(5))); + Node[] lists = {list1}; + + MergeKSortedLinkedList merger = new MergeKSortedLinkedList(); + Node mergedHead = merger.mergeKList(lists, lists.length); + + int[] expectedValues = {1, 3, 5}; + int[] actualValues = getListValues(mergedHead); + assertArrayEquals(expectedValues, actualValues, "Merged list should match the single input list when only one list is provided."); + } + + @Test + void testMergeListsOfDifferentLengths() { + Node list1 = new Node(1, new Node(3, new Node(5))); + Node list2 = new Node(2, new Node(4)); + Node list3 = new Node(6); + Node[] lists = {list1, list2, list3}; + + MergeKSortedLinkedList merger = new MergeKSortedLinkedList(); + Node mergedHead = merger.mergeKList(lists, lists.length); + + int[] expectedValues = {1, 2, 3, 4, 5, 6}; + int[] actualValues = getListValues(mergedHead); + assertArrayEquals(expectedValues, actualValues, "Merged list values do not match expected sorted order for lists of different lengths."); + } + + @Test + void testMergeSingleElementLists() { + Node list1 = new Node(1); + Node list2 = new Node(3); + Node list3 = new Node(2); + Node[] lists = {list1, list2, list3}; + + MergeKSortedLinkedList merger = new MergeKSortedLinkedList(); + Node mergedHead = merger.mergeKList(lists, lists.length); + + int[] expectedValues = {1, 2, 3}; + int[] actualValues = getListValues(mergedHead); + assertArrayEquals(expectedValues, actualValues, "Merged list values do not match expected sorted order for single-element lists."); + } + + /** + * Helper method to extract values from the linked list into an array for assertion. + */ + private int[] getListValues(Node head) { + int[] values = new int[100]; // assuming max length for simplicity + int i = 0; + Node curr = head; + while (curr != null) { + values[i++] = curr.data; + curr = curr.next; + } + return Arrays.copyOf(values, i); // return only filled part + } +}