Enhance docs, add tests in LeftistHeap (#5982)

This commit is contained in:
Hardik Pawar
2024-10-26 20:58:55 +05:30
committed by GitHub
parent 709d9771e2
commit 20239f20f1
2 changed files with 133 additions and 37 deletions

View File

@ -2,24 +2,33 @@ package com.thealgorithms.datastructures.heaps;
import java.util.ArrayList; import java.util.ArrayList;
/* /**
* This is a leftist heap that follows the same operations as a * This class implements a Leftist Heap, which is a type of priority queue
* binary min heap, but may be unbalanced at times and follows a * that follows similar operations to a binary min-heap but allows for
* leftist property, in which the left side is more heavy on the * unbalanced structures based on the leftist property.
* right based on the null-path length (npl) values.
* *
* Source: https://iq.opengenus.org/leftist-heap/ * <p>
* A Leftist Heap maintains the leftist property, which ensures that the
* left subtree is heavier than the right subtree based on the
* null-path length (npl) values. This allows for efficient merging
* of heaps and supports operations like insertion, extraction of
* the minimum element, and in-order traversal.
* </p>
* *
* <p>
* For more information on Leftist Heaps, visit:
* <a href="https://iq.opengenus.org/leftist-heap/">OpenGenus</a>
* </p>
*/ */
public class LeftistHeap { public class LeftistHeap {
// Node class representing each element in the Leftist Heap
private static final class Node { private static final class Node {
private final int element; private final int element;
private int npl; private int npl;
private Node left; private Node left;
private Node right; private Node right;
// Node constructor setting the data element and left/right pointers to null // Node constructor that initializes the element and sets child pointers to null
private Node(int element) { private Node(int element) {
this.element = element; this.element = element;
left = null; left = null;
@ -30,31 +39,45 @@ public class LeftistHeap {
private Node root; private Node root;
// Constructor // Constructor initializing an empty Leftist Heap
public LeftistHeap() { public LeftistHeap() {
root = null; root = null;
} }
// Checks if heap is empty /**
* Checks if the heap is empty.
*
* @return true if the heap is empty; false otherwise
*/
public boolean isEmpty() { public boolean isEmpty() {
return root == null; return root == null;
} }
// Resets structure to initial state /**
* Resets the heap to its initial state, effectively clearing all elements.
*/
public void clear() { public void clear() {
// We will put head is null root = null; // Set root to null to clear the heap
root = null;
} }
// Merge function that merges the contents of another leftist heap with the /**
// current one * Merges the contents of another Leftist Heap into this one.
*
* @param h1 the LeftistHeap to be merged into this heap
*/
public void merge(LeftistHeap h1) { public void merge(LeftistHeap h1) {
// If the present function is rhs then we ignore the merge // Merge the current heap with the provided heap and set the provided heap's root to null
root = merge(root, h1.root); root = merge(root, h1.root);
h1.root = null; h1.root = null;
} }
// Function merge with two Nodes a and b /**
* Merges two nodes, maintaining the leftist property.
*
* @param a the first node
* @param b the second node
* @return the merged node maintaining the leftist property
*/
public Node merge(Node a, Node b) { public Node merge(Node a, Node b) {
if (a == null) { if (a == null) {
return b; return b;
@ -64,17 +87,17 @@ public class LeftistHeap {
return a; return a;
} }
// Violates leftist property, so must do a swap // Ensure that the leftist property is maintained
if (a.element > b.element) { if (a.element > b.element) {
Node temp = a; Node temp = a;
a = b; a = b;
b = temp; b = temp;
} }
// Now we call the function merge to merge a and b // Merge the right child of node a with node b
a.right = merge(a.right, b); a.right = merge(a.right, b);
// Violates leftist property so must swap here // If left child is null, make right child the left child
if (a.left == null) { if (a.left == null) {
a.left = a.right; a.left = a.right;
a.right = null; a.right = null;
@ -89,14 +112,21 @@ public class LeftistHeap {
return a; return a;
} }
// Function insert. Uses the merge function to add the data /**
* Inserts a new element into the Leftist Heap.
*
* @param a the element to be inserted
*/
public void insert(int a) { public void insert(int a) {
root = merge(new Node(a), root); root = merge(new Node(a), root);
} }
// Returns and removes the minimum element in the heap /**
* Extracts and removes the minimum element from the heap.
*
* @return the minimum element in the heap, or -1 if the heap is empty
*/
public int extractMin() { public int extractMin() {
// If is empty return -1
if (isEmpty()) { if (isEmpty()) {
return -1; return -1;
} }
@ -106,14 +136,23 @@ public class LeftistHeap {
return min; return min;
} }
// Function returning a list of an in order traversal of the data structure /**
* Returns a list of the elements in the heap in in-order traversal.
*
* @return an ArrayList containing the elements in in-order
*/
public ArrayList<Integer> inOrder() { public ArrayList<Integer> inOrder() {
ArrayList<Integer> lst = new ArrayList<>(); ArrayList<Integer> lst = new ArrayList<>();
inOrderAux(root, lst); inOrderAux(root, lst);
return new ArrayList<>(lst); return new ArrayList<>(lst);
} }
// Auxiliary function for in_order /**
* Auxiliary function for in-order traversal
*
* @param n the current node
* @param lst the list to store the elements in in-order
*/
private void inOrderAux(Node n, ArrayList<Integer> lst) { private void inOrderAux(Node n, ArrayList<Integer> lst) {
if (n == null) { if (n == null) {
return; return;

View File

@ -6,23 +6,80 @@ import org.junit.jupiter.api.Test;
public class LeftistHeapTest { public class LeftistHeapTest {
@Test @Test
void testLeftistHeap() { void testIsEmpty() {
LeftistHeap heap = new LeftistHeap();
Assertions.assertTrue(heap.isEmpty(), "Heap should be empty initially.");
heap.insert(10);
Assertions.assertFalse(heap.isEmpty(), "Heap should not be empty after insertion.");
heap.clear();
Assertions.assertTrue(heap.isEmpty(), "Heap should be empty after clearing.");
}
@Test
void testInsertAndExtractMin() {
LeftistHeap heap = new LeftistHeap(); LeftistHeap heap = new LeftistHeap();
Assertions.assertTrue(heap.isEmpty());
heap.insert(6); heap.insert(6);
Assertions.assertTrue(!heap.isEmpty());
heap.insert(2); heap.insert(2);
heap.insert(3); heap.insert(3);
heap.insert(1); heap.insert(1);
heap.inOrder();
Assertions.assertTrue(heap.inOrder().toString().equals("[6, 2, 3, 1]")); Assertions.assertEquals(1, heap.extractMin(), "Minimum should be 1.");
Assertions.assertTrue(heap.extractMin() == 1); Assertions.assertEquals(2, heap.extractMin(), "Next minimum should be 2.");
Assertions.assertTrue(heap.inOrder().toString().equals("[6, 2, 3]")); Assertions.assertEquals(3, heap.extractMin(), "Next minimum should be 3.");
Assertions.assertEquals(6, heap.extractMin(), "Next minimum should be 6.");
Assertions.assertEquals(-1, heap.extractMin(), "Extracting from an empty heap should return -1.");
}
@Test
void testMerge() {
LeftistHeap heap1 = new LeftistHeap();
heap1.insert(1);
heap1.insert(3);
heap1.insert(5);
LeftistHeap heap2 = new LeftistHeap();
heap2.insert(2);
heap2.insert(4);
heap2.insert(6);
heap1.merge(heap2);
Assertions.assertEquals(1, heap1.extractMin(), "After merging, minimum should be 1.");
Assertions.assertEquals(2, heap1.extractMin(), "Next minimum should be 2.");
Assertions.assertEquals(3, heap1.extractMin(), "Next minimum should be 3.");
Assertions.assertEquals(4, heap1.extractMin(), "Next minimum should be 4.");
Assertions.assertEquals(5, heap1.extractMin(), "Next minimum should be 5.");
Assertions.assertEquals(6, heap1.extractMin(), "Next minimum should be 6.");
Assertions.assertEquals(-1, heap1.extractMin(), "Extracting from an empty heap should return -1.");
}
@Test
void testInOrderTraversal() {
LeftistHeap heap = new LeftistHeap();
heap.insert(10);
heap.insert(5);
heap.insert(20);
heap.insert(15);
heap.insert(30);
Assertions.assertEquals("[20, 15, 30, 5, 10]", heap.inOrder().toString(), "In-order traversal should match the expected output.");
}
@Test
void testMultipleExtractions() {
LeftistHeap heap = new LeftistHeap();
heap.insert(10);
heap.insert(5);
heap.insert(3);
heap.insert(8); heap.insert(8);
heap.insert(12);
heap.insert(4); // Extract multiple elements
Assertions.assertTrue(heap.inOrder().toString().equals("[8, 3, 12, 2, 6, 4]")); Assertions.assertEquals(3, heap.extractMin());
heap.clear(); Assertions.assertEquals(5, heap.extractMin());
Assertions.assertTrue(heap.isEmpty()); Assertions.assertEquals(8, heap.extractMin());
Assertions.assertEquals(10, heap.extractMin());
Assertions.assertEquals(-1, heap.extractMin(), "Extracting from an empty heap should return -1.");
} }
} }