Skip to content

refactor: Enhance docs, add tests in LeftistHeap #5982

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 6 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,33 @@

import java.util.ArrayList;

/*
* This is a leftist heap that follows the same operations as a
* binary min heap, but may be unbalanced at times and follows a
* leftist property, in which the left side is more heavy on the
* right based on the null-path length (npl) values.
/**
* This class implements a Leftist Heap, which is a type of priority queue
* that follows similar operations to a binary min-heap but allows for
* unbalanced structures based on the leftist property.
*
* 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 {
// Node class representing each element in the Leftist Heap
private static final class Node {
private final int element;
private int npl;
private Node left;
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) {
this.element = element;
left = null;
Expand All @@ -30,31 +39,45 @@ private Node(int element) {

private Node root;

// Constructor
// Constructor initializing an empty Leftist Heap
public LeftistHeap() {
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() {
return root == null;
}

// Resets structure to initial state
/**
* Resets the heap to its initial state, effectively clearing all elements.
*/
public void clear() {
// We will put head is null
root = null;
root = null; // Set root to null to clear the heap
}

// 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) {
// 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);
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) {
if (a == null) {
return b;
Expand All @@ -64,17 +87,17 @@ public Node merge(Node a, Node b) {
return a;
}

// Violates leftist property, so must do a swap
// Ensure that the leftist property is maintained
if (a.element > b.element) {
Node temp = a;
a = b;
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);

// Violates leftist property so must swap here
// If left child is null, make right child the left child
if (a.left == null) {
a.left = a.right;
a.right = null;
Expand All @@ -89,14 +112,21 @@ public Node merge(Node a, Node b) {
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) {
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() {
// If is empty return -1
if (isEmpty()) {
return -1;
}
Expand All @@ -106,14 +136,23 @@ public int extractMin() {
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() {
ArrayList<Integer> lst = new ArrayList<>();
inOrderAux(root, 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) {
if (n == null) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,80 @@
public class LeftistHeapTest {

@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();
Assertions.assertTrue(heap.isEmpty());
heap.insert(6);
Assertions.assertTrue(!heap.isEmpty());
heap.insert(2);
heap.insert(3);
heap.insert(1);
heap.inOrder();
Assertions.assertTrue(heap.inOrder().toString().equals("[6, 2, 3, 1]"));
Assertions.assertTrue(heap.extractMin() == 1);
Assertions.assertTrue(heap.inOrder().toString().equals("[6, 2, 3]"));

Assertions.assertEquals(1, heap.extractMin(), "Minimum should be 1.");
Assertions.assertEquals(2, heap.extractMin(), "Next minimum should be 2.");
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(12);
heap.insert(4);
Assertions.assertTrue(heap.inOrder().toString().equals("[8, 3, 12, 2, 6, 4]"));
heap.clear();
Assertions.assertTrue(heap.isEmpty());

// Extract multiple elements
Assertions.assertEquals(3, heap.extractMin());
Assertions.assertEquals(5, heap.extractMin());
Assertions.assertEquals(8, heap.extractMin());
Assertions.assertEquals(10, heap.extractMin());
Assertions.assertEquals(-1, heap.extractMin(), "Extracting from an empty heap should return -1.");
}
}