Skip to content

Commit 03bb8ee

Browse files
authored
Enhance docs, add tests in MaxHeap (#5983)
1 parent b14db81 commit 03bb8ee

File tree

4 files changed

+321
-58
lines changed

4 files changed

+321
-58
lines changed

DIRECTORY.md

+3
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,7 @@
488488
* [PalindromePrime](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/PalindromePrime.java)
489489
* [PalindromeSinglyLinkedList](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/PalindromeSinglyLinkedList.java)
490490
* [RangeInSortedArray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/RangeInSortedArray.java)
491+
* [ShuffleArray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/ShuffleArray.java)
491492
* [Sparsity](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/Sparsity.java)
492493
* [ThreeSumProblem](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/ThreeSumProblem.java)
493494
* [TwoSumProblem](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/TwoSumProblem.java)
@@ -876,6 +877,7 @@
876877
* [HeapElementTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/HeapElementTest.java)
877878
* [KthElementFinderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/KthElementFinderTest.java)
878879
* [LeftistHeapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/LeftistHeapTest.java)
880+
* [MaxHeapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/MaxHeapTest.java)
879881
* [MedianFinderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/MedianFinderTest.java)
880882
* [MergeKSortedArraysTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/MergeKSortedArraysTest.java)
881883
* [MinHeapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/MinHeapTest.java)
@@ -1136,6 +1138,7 @@
11361138
* [PalindromePrimeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/PalindromePrimeTest.java)
11371139
* [PalindromeSinglyLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/PalindromeSinglyLinkedListTest.java)
11381140
* [RangeInSortedArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/RangeInSortedArrayTest.java)
1141+
* [ShuffleArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/ShuffleArrayTest.java)
11391142
* [SparsityTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/SparsityTest.java)
11401143
* [ThreeSumProblemTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/ThreeSumProblemTest.java)
11411144
* [TwoSumProblemTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/TwoSumProblemTest.java)

src/main/java/com/thealgorithms/datastructures/heaps/HeapElement.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,9 @@ public String toString() {
143143
}
144144

145145
/**
146-
* Compares this heap element to another object for equality.
147-
*
148-
* @param o the object to compare with
149-
* @return true if the keys and additional information are identical, false otherwise
146+
* @param o : an object to compare with the current element
147+
* @return true if the keys on both elements are identical and the
148+
* additional info objects are identical.
150149
*/
151150
@Override
152151
public boolean equals(Object o) {

src/main/java/com/thealgorithms/datastructures/heaps/MaxHeap.java

+171-54
Original file line numberDiff line numberDiff line change
@@ -4,128 +4,245 @@
44
import java.util.List;
55

66
/**
7-
* Heap tree where a node's key is higher than or equal to its parent's and
8-
* lower than or equal to its children's.
7+
* A Max Heap implementation where each node's key is higher than or equal to its children's keys.
8+
* This data structure provides O(log n) time complexity for insertion and deletion operations,
9+
* and O(1) for retrieving the maximum element.
10+
*
11+
* Properties:
12+
* 1. Complete Binary Tree
13+
* 2. Parent node's key ≥ Children nodes' keys
14+
* 3. Root contains the maximum element
15+
*
16+
* Example usage:
17+
* <pre>
18+
* List<HeapElement> elements = Arrays.asList(
19+
* new HeapElement(5, "Five"),
20+
* new HeapElement(2, "Two")
21+
* );
22+
* MaxHeap heap = new MaxHeap(elements);
23+
* heap.insertElement(new HeapElement(7, "Seven"));
24+
* HeapElement max = heap.getElement(); // Returns and removes the maximum element
25+
* </pre>
926
*
1027
* @author Nicolas Renard
1128
*/
1229
public class MaxHeap implements Heap {
1330

31+
/** The internal list that stores heap elements */
1432
private final List<HeapElement> maxHeap;
1533

34+
/**
35+
* Constructs a new MaxHeap from a list of elements.
36+
* Null elements in the input list are ignored.
37+
*
38+
* @param listElements List of HeapElement objects to initialize the heap
39+
* @throws IllegalArgumentException if the input list is null
40+
*/
1641
public MaxHeap(List<HeapElement> listElements) {
42+
if (listElements == null) {
43+
throw new IllegalArgumentException("Input list cannot be null");
44+
}
45+
1746
maxHeap = new ArrayList<>();
47+
48+
// Safe initialization: directly add non-null elements first
1849
for (HeapElement heapElement : listElements) {
1950
if (heapElement != null) {
20-
insertElement(heapElement);
21-
} else {
22-
System.out.println("Null element. Not added to heap");
51+
maxHeap.add(heapElement);
2352
}
2453
}
25-
if (maxHeap.isEmpty()) {
26-
System.out.println("No element has been added, empty heap.");
54+
55+
// Heapify the array bottom-up
56+
for (int i = maxHeap.size() / 2; i >= 0; i--) {
57+
heapifyDown(i + 1); // +1 because heapifyDown expects 1-based index
58+
}
59+
}
60+
61+
/**
62+
* Maintains heap properties by moving an element down the heap.
63+
* Similar to toggleDown but used specifically during initialization.
64+
*
65+
* @param elementIndex 1-based index of the element to heapify
66+
*/
67+
private void heapifyDown(int elementIndex) {
68+
int largest = elementIndex - 1;
69+
int leftChild = 2 * elementIndex - 1;
70+
int rightChild = 2 * elementIndex;
71+
72+
if (leftChild < maxHeap.size() && maxHeap.get(leftChild).getKey() > maxHeap.get(largest).getKey()) {
73+
largest = leftChild;
74+
}
75+
76+
if (rightChild < maxHeap.size() && maxHeap.get(rightChild).getKey() > maxHeap.get(largest).getKey()) {
77+
largest = rightChild;
78+
}
79+
80+
if (largest != elementIndex - 1) {
81+
HeapElement swap = maxHeap.get(elementIndex - 1);
82+
maxHeap.set(elementIndex - 1, maxHeap.get(largest));
83+
maxHeap.set(largest, swap);
84+
85+
heapifyDown(largest + 1);
2786
}
2887
}
2988

3089
/**
31-
* Get the element at a given index. The key for the list is equal to index
32-
* value - 1
90+
* Retrieves the element at the specified index without removing it.
91+
* Note: The index is 1-based for consistency with heap operations.
3392
*
34-
* @param elementIndex index
35-
* @return heapElement
93+
* @param elementIndex 1-based index of the element to retrieve
94+
* @return HeapElement at the specified index
95+
* @throws IndexOutOfBoundsException if the index is invalid
3696
*/
3797
public HeapElement getElement(int elementIndex) {
3898
if ((elementIndex <= 0) || (elementIndex > maxHeap.size())) {
39-
throw new IndexOutOfBoundsException("Index out of heap range");
99+
throw new IndexOutOfBoundsException("Index " + elementIndex + " is out of heap range [1, " + maxHeap.size() + "]");
40100
}
41101
return maxHeap.get(elementIndex - 1);
42102
}
43103

44-
// Get the key of the element at a given index
104+
/**
105+
* Retrieves the key value of an element at the specified index.
106+
*
107+
* @param elementIndex 1-based index of the element
108+
* @return double value representing the key
109+
* @throws IndexOutOfBoundsException if the index is invalid
110+
*/
45111
private double getElementKey(int elementIndex) {
46112
if ((elementIndex <= 0) || (elementIndex > maxHeap.size())) {
47-
throw new IndexOutOfBoundsException("Index out of heap range");
113+
throw new IndexOutOfBoundsException("Index " + elementIndex + " is out of heap range [1, " + maxHeap.size() + "]");
48114
}
49-
50115
return maxHeap.get(elementIndex - 1).getKey();
51116
}
52117

53-
// Swaps two elements in the heap
118+
/**
119+
* Swaps two elements in the heap.
120+
*
121+
* @param index1 1-based index of first element
122+
* @param index2 1-based index of second element
123+
*/
54124
private void swap(int index1, int index2) {
55125
HeapElement temporaryElement = maxHeap.get(index1 - 1);
56126
maxHeap.set(index1 - 1, maxHeap.get(index2 - 1));
57127
maxHeap.set(index2 - 1, temporaryElement);
58128
}
59129

60-
// Toggle an element up to its right place as long as its key is lower than its parent's
130+
/**
131+
* Moves an element up the heap until heap properties are satisfied.
132+
* This operation is called after insertion to maintain heap properties.
133+
*
134+
* @param elementIndex 1-based index of the element to move up
135+
*/
61136
private void toggleUp(int elementIndex) {
62137
double key = maxHeap.get(elementIndex - 1).getKey();
63-
while (getElementKey((int) Math.floor(elementIndex / 2.0)) < key) {
138+
while (elementIndex > 1 && getElementKey((int) Math.floor(elementIndex / 2.0)) < key) {
64139
swap(elementIndex, (int) Math.floor(elementIndex / 2.0));
65140
elementIndex = (int) Math.floor(elementIndex / 2.0);
66141
}
67142
}
68143

69-
// Toggle an element down to its right place as long as its key is higher
70-
// than any of its children's
144+
/**
145+
* Moves an element down the heap until heap properties are satisfied.
146+
* This operation is called after deletion to maintain heap properties.
147+
*
148+
* @param elementIndex 1-based index of the element to move down
149+
*/
71150
private void toggleDown(int elementIndex) {
72151
double key = maxHeap.get(elementIndex - 1).getKey();
73-
boolean wrongOrder = (key < getElementKey(elementIndex * 2)) || (key < getElementKey(Math.min(elementIndex * 2, maxHeap.size())));
74-
while ((2 * elementIndex <= maxHeap.size()) && wrongOrder) {
75-
// Check whether it shall swap the element with its left child or its right one if any.
76-
if ((2 * elementIndex < maxHeap.size()) && (getElementKey(elementIndex * 2 + 1) > getElementKey(elementIndex * 2))) {
77-
swap(elementIndex, 2 * elementIndex + 1);
78-
elementIndex = 2 * elementIndex + 1;
152+
boolean wrongOrder = (2 * elementIndex <= maxHeap.size() && key < getElementKey(elementIndex * 2)) || (2 * elementIndex + 1 <= maxHeap.size() && key < getElementKey(elementIndex * 2 + 1));
153+
154+
while (2 * elementIndex <= maxHeap.size() && wrongOrder) {
155+
int largerChildIndex;
156+
if (2 * elementIndex + 1 <= maxHeap.size() && getElementKey(elementIndex * 2 + 1) > getElementKey(elementIndex * 2)) {
157+
largerChildIndex = 2 * elementIndex + 1;
79158
} else {
80-
swap(elementIndex, 2 * elementIndex);
81-
elementIndex = 2 * elementIndex;
159+
largerChildIndex = 2 * elementIndex;
82160
}
83-
wrongOrder = (key < getElementKey(elementIndex * 2)) || (key < getElementKey(Math.min(elementIndex * 2, maxHeap.size())));
161+
162+
swap(elementIndex, largerChildIndex);
163+
elementIndex = largerChildIndex;
164+
165+
wrongOrder = (2 * elementIndex <= maxHeap.size() && key < getElementKey(elementIndex * 2)) || (2 * elementIndex + 1 <= maxHeap.size() && key < getElementKey(elementIndex * 2 + 1));
84166
}
85167
}
86168

87-
private HeapElement extractMax() {
88-
HeapElement result = maxHeap.get(0);
89-
deleteElement(0);
169+
/**
170+
* Extracts and returns the maximum element from the heap.
171+
*
172+
* @return HeapElement with the highest key
173+
* @throws EmptyHeapException if the heap is empty
174+
*/
175+
private HeapElement extractMax() throws EmptyHeapException {
176+
if (maxHeap.isEmpty()) {
177+
throw new EmptyHeapException("Cannot extract from an empty heap");
178+
}
179+
HeapElement result = maxHeap.getFirst();
180+
deleteElement(1);
90181
return result;
91182
}
92183

184+
/**
185+
* {@inheritDoc}
186+
*/
93187
@Override
94-
public final void insertElement(HeapElement element) {
188+
public void insertElement(HeapElement element) {
189+
if (element == null) {
190+
throw new IllegalArgumentException("Cannot insert null element");
191+
}
95192
maxHeap.add(element);
96193
toggleUp(maxHeap.size());
97194
}
98195

196+
/**
197+
* {@inheritDoc}
198+
*/
99199
@Override
100-
public void deleteElement(int elementIndex) {
200+
public void deleteElement(int elementIndex) throws EmptyHeapException {
101201
if (maxHeap.isEmpty()) {
102-
try {
103-
throw new EmptyHeapException("Attempt to delete an element from an empty heap");
104-
} catch (EmptyHeapException e) {
105-
e.printStackTrace();
106-
}
202+
throw new EmptyHeapException("Cannot delete from an empty heap");
107203
}
108204
if ((elementIndex > maxHeap.size()) || (elementIndex <= 0)) {
109-
throw new IndexOutOfBoundsException("Index out of heap range");
205+
throw new IndexOutOfBoundsException("Index " + elementIndex + " is out of heap range [1, " + maxHeap.size() + "]");
110206
}
111-
// The last element in heap replaces the one to be deleted
112-
maxHeap.set(elementIndex - 1, getElement(maxHeap.size()));
113-
maxHeap.remove(maxHeap.size());
114-
// Shall the new element be moved up...
115-
if (getElementKey(elementIndex) > getElementKey((int) Math.floor(elementIndex / 2.0))) {
116-
toggleUp(elementIndex);
117-
} // ... or down ?
118-
else if (((2 * elementIndex <= maxHeap.size()) && (getElementKey(elementIndex) < getElementKey(elementIndex * 2))) || ((2 * elementIndex < maxHeap.size()) && (getElementKey(elementIndex) < getElementKey(elementIndex * 2)))) {
119-
toggleDown(elementIndex);
207+
208+
// Replace with last element and remove last position
209+
maxHeap.set(elementIndex - 1, maxHeap.getLast());
210+
maxHeap.removeLast();
211+
212+
// No need to toggle if we just removed the last element
213+
if (!maxHeap.isEmpty() && elementIndex <= maxHeap.size()) {
214+
// Determine whether to toggle up or down
215+
if (elementIndex > 1 && getElementKey(elementIndex) > getElementKey((int) Math.floor(elementIndex / 2.0))) {
216+
toggleUp(elementIndex);
217+
} else {
218+
toggleDown(elementIndex);
219+
}
120220
}
121221
}
122222

223+
/**
224+
* {@inheritDoc}
225+
*/
123226
@Override
124227
public HeapElement getElement() throws EmptyHeapException {
125-
try {
126-
return extractMax();
127-
} catch (Exception e) {
128-
throw new EmptyHeapException("Heap is empty. Error retrieving element", e);
129-
}
228+
return extractMax();
229+
}
230+
231+
/**
232+
* Returns the current size of the heap.
233+
*
234+
* @return number of elements in the heap
235+
*/
236+
public int size() {
237+
return maxHeap.size();
238+
}
239+
240+
/**
241+
* Checks if the heap is empty.
242+
*
243+
* @return true if the heap contains no elements
244+
*/
245+
public boolean isEmpty() {
246+
return maxHeap.isEmpty();
130247
}
131248
}

0 commit comments

Comments
 (0)