mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-07-06 00:54:32 +08:00
Enhance docs, add tests in LeftistHeap
(#5982)
This commit is contained in:
@ -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;
|
||||||
|
@ -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.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user