Skip to content

refactor: Enhance docs, add tests in MergeKSortedLinkedList #5995

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
* [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)
* [ReverseKGroupTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/ReverseKGroupTest.java)
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
*
* <p>Example usage:
* <pre>
* 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);
* </pre>
* </p>
*
* <p>This class is designed to handle nodes of integer linked lists and can be expanded for additional data types if needed.</p>
*
* @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
* <p>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.</p>
*
* @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<Node> 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<Node> minHeap = new PriorityQueue<>(Comparator.comparingInt(x -> x.data));

// Make new head among smallest heads in K linkedList
Node head = min.poll();
min.add(head.next);
Node curr = head;
// 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);
}

// merging LinkedList
while (!min.isEmpty()) {
Node temp = min.poll();
Node curr = head;
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;

Node(int data) {
this.data = data;
this.next = null;
}

private int data;
private Node next;
Node(int data, Node next) {
this.data = data;
this.next = next;
}
}
}
Original file line number Diff line number Diff line change
@@ -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
}
}