Skip to content

Commit a78b15d

Browse files
authored
Enhance docs, add tests in MinHeap (TheAlgorithms#5985)
1 parent bc6ea1e commit a78b15d

File tree

5 files changed

+344
-52
lines changed

5 files changed

+344
-52
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,7 @@
871871
* [LeftistHeapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/LeftistHeapTest.java)
872872
* [MedianFinderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/MedianFinderTest.java)
873873
* [MergeKSortedArraysTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/MergeKSortedArraysTest.java)
874+
* [MinHeapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/MinHeapTest.java)
874875
* [MinPriorityQueueTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/MinPriorityQueueTest.java)
875876
* lists
876877
* [CircleLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/CircleLinkedListTest.java)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,5 @@ public interface Heap {
4040
* @param elementIndex int containing the position in the heap of the
4141
* element to be deleted.
4242
*/
43-
void deleteElement(int elementIndex);
43+
void deleteElement(int elementIndex) throws EmptyHeapException;
4444
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,8 @@ public int hashCode() {
167167
result += (additionalInfo != null) ? additionalInfo.hashCode() : 0;
168168
return result;
169169
}
170+
171+
public String getValue() {
172+
return additionalInfo.toString();
173+
}
170174
}

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

Lines changed: 197 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,122 +4,268 @@
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 Min Heap implementation where each node's key is lower 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 minimum element.
10+
*
11+
* Properties:
12+
* 1. Complete Binary Tree
13+
* 2. Parent node's key ≤ Children nodes' keys
14+
* 3. Root contains the minimum element
15+
*
16+
* Example usage:
17+
* ```java
18+
* List<HeapElement> elements = Arrays.asList(
19+
* new HeapElement(5, "Five"),
20+
* new HeapElement(2, "Two")
21+
* );
22+
* MinHeap heap = new MinHeap(elements);
23+
* heap.insertElement(new HeapElement(1, "One"));
24+
* HeapElement min = heap.getElement(); // Returns and removes the minimum element
25+
* ```
926
*
1027
* @author Nicolas Renard
1128
*/
1229
public class MinHeap implements Heap {
1330

1431
private final List<HeapElement> minHeap;
1532

33+
/**
34+
* Constructs a new MinHeap from a list of elements.
35+
* Null elements in the input list are ignored with a warning message.
36+
*
37+
* @param listElements List of HeapElement objects to initialize the heap
38+
* @throws IllegalArgumentException if the input list is null
39+
*/
1640
public MinHeap(List<HeapElement> listElements) {
41+
if (listElements == null) {
42+
throw new IllegalArgumentException("Input list cannot be null");
43+
}
44+
1745
minHeap = new ArrayList<>();
46+
47+
// Safe initialization: directly add elements first
1848
for (HeapElement heapElement : listElements) {
1949
if (heapElement != null) {
20-
insertElement(heapElement);
50+
minHeap.add(heapElement);
2151
} else {
2252
System.out.println("Null element. Not added to heap");
2353
}
2454
}
55+
56+
// Heapify the array bottom-up
57+
for (int i = minHeap.size() / 2; i >= 0; i--) {
58+
heapifyDown(i + 1);
59+
}
60+
2561
if (minHeap.isEmpty()) {
2662
System.out.println("No element has been added, empty heap.");
2763
}
2864
}
2965

30-
// Get the element at a given index. The key for the list is equal to index value - 1
66+
/**
67+
* Retrieves the element at the specified index without removing it.
68+
* Note: The index is 1-based for consistency with heap operations.
69+
*
70+
* @param elementIndex 1-based index of the element to retrieve
71+
* @return HeapElement at the specified index
72+
* @throws IndexOutOfBoundsException if the index is invalid
73+
*/
3174
public HeapElement getElement(int elementIndex) {
3275
if ((elementIndex <= 0) || (elementIndex > minHeap.size())) {
33-
throw new IndexOutOfBoundsException("Index out of heap range");
76+
throw new IndexOutOfBoundsException("Index " + elementIndex + " is out of heap range [1, " + minHeap.size() + "]");
3477
}
3578
return minHeap.get(elementIndex - 1);
3679
}
3780

38-
// Get the key of the element at a given index
81+
/**
82+
* Retrieves the key value of an element at the specified index.
83+
*
84+
* @param elementIndex 1-based index of the element
85+
* @return double value representing the key
86+
* @throws IndexOutOfBoundsException if the index is invalid
87+
*/
3988
private double getElementKey(int elementIndex) {
4089
if ((elementIndex <= 0) || (elementIndex > minHeap.size())) {
41-
throw new IndexOutOfBoundsException("Index out of heap range");
90+
throw new IndexOutOfBoundsException("Index " + elementIndex + " is out of heap range [1, " + minHeap.size() + "]");
4291
}
43-
4492
return minHeap.get(elementIndex - 1).getKey();
4593
}
4694

47-
// Swaps two elements in the heap
95+
/**
96+
* Swaps two elements in the heap.
97+
*
98+
* @param index1 1-based index of first element
99+
* @param index2 1-based index of second element
100+
*/
48101
private void swap(int index1, int index2) {
49102
HeapElement temporaryElement = minHeap.get(index1 - 1);
50103
minHeap.set(index1 - 1, minHeap.get(index2 - 1));
51104
minHeap.set(index2 - 1, temporaryElement);
52105
}
53106

54-
// Toggle an element up to its right place as long as its key is lower than its parent's
107+
/**
108+
* Maintains heap properties by moving an element down the heap.
109+
* Used specifically during initialization.
110+
*
111+
* @param elementIndex 1-based index of the element to heapify
112+
*/
113+
private void heapifyDown(int elementIndex) {
114+
int smallest = elementIndex - 1; // Convert to 0-based index
115+
int leftChild = 2 * elementIndex - 1;
116+
int rightChild = 2 * elementIndex;
117+
118+
// Check if left child is smaller than root
119+
if (leftChild < minHeap.size() && minHeap.get(leftChild).getKey() < minHeap.get(smallest).getKey()) {
120+
smallest = leftChild;
121+
}
122+
123+
// Check if right child is smaller than smallest so far
124+
if (rightChild < minHeap.size() && minHeap.get(rightChild).getKey() < minHeap.get(smallest).getKey()) {
125+
smallest = rightChild;
126+
}
127+
128+
// If smallest is not root
129+
if (smallest != elementIndex - 1) {
130+
HeapElement swap = minHeap.get(elementIndex - 1);
131+
minHeap.set(elementIndex - 1, minHeap.get(smallest));
132+
minHeap.set(smallest, swap);
133+
134+
// Recursively heapify the affected sub-tree
135+
heapifyDown(smallest + 1); // Convert back to 1-based index
136+
}
137+
}
138+
139+
/**
140+
* Moves an element up the heap until heap properties are satisfied.
141+
* This operation is called after insertion to maintain heap properties.
142+
*
143+
* @param elementIndex 1-based index of the element to move up
144+
*/
55145
private void toggleUp(int elementIndex) {
146+
if (elementIndex <= 1) {
147+
return;
148+
}
149+
56150
double key = minHeap.get(elementIndex - 1).getKey();
57-
while (getElementKey((int) Math.floor(elementIndex / 2.0) + 1) > key) {
58-
swap(elementIndex, (int) Math.floor(elementIndex / 2.0));
59-
elementIndex = (int) Math.floor(elementIndex / 2.0);
151+
int parentIndex = (int) Math.floor(elementIndex / 2.0);
152+
153+
while (elementIndex > 1 && getElementKey(parentIndex) > key) {
154+
swap(elementIndex, parentIndex);
155+
elementIndex = parentIndex;
156+
parentIndex = (int) Math.floor(elementIndex / 2.0);
60157
}
61158
}
62159

63-
// Toggle an element down to its right place as long as its key is higher
64-
// than any of its children's
160+
/**
161+
* Moves an element down the heap until heap properties are satisfied.
162+
* This operation is called after deletion to maintain heap properties.
163+
*
164+
* @param elementIndex 1-based index of the element to move down
165+
*/
65166
private void toggleDown(int elementIndex) {
66167
double key = minHeap.get(elementIndex - 1).getKey();
67-
boolean wrongOrder = (key > getElementKey(elementIndex * 2)) || (key > getElementKey(Math.min(elementIndex * 2, minHeap.size())));
68-
while ((2 * elementIndex <= minHeap.size()) && wrongOrder) {
69-
// Check whether it shall swap the element with its left child or its right one if any.
70-
if ((2 * elementIndex < minHeap.size()) && (getElementKey(elementIndex * 2 + 1) < getElementKey(elementIndex * 2))) {
71-
swap(elementIndex, 2 * elementIndex + 1);
72-
elementIndex = 2 * elementIndex + 1;
73-
} else {
74-
swap(elementIndex, 2 * elementIndex);
75-
elementIndex = 2 * elementIndex;
168+
int size = minHeap.size();
169+
170+
while (true) {
171+
int smallest = elementIndex;
172+
int leftChild = 2 * elementIndex;
173+
int rightChild = 2 * elementIndex + 1;
174+
175+
if (leftChild <= size && getElementKey(leftChild) < key) {
176+
smallest = leftChild;
177+
}
178+
179+
if (rightChild <= size && getElementKey(rightChild) < getElementKey(smallest)) {
180+
smallest = rightChild;
181+
}
182+
183+
if (smallest == elementIndex) {
184+
break;
76185
}
77-
wrongOrder = (key > getElementKey(elementIndex * 2)) || (key > getElementKey(Math.min(elementIndex * 2, minHeap.size())));
186+
187+
swap(elementIndex, smallest);
188+
elementIndex = smallest;
78189
}
79190
}
80191

81-
private HeapElement extractMin() {
82-
HeapElement result = minHeap.get(0);
83-
deleteElement(0);
192+
/**
193+
* Extracts and returns the minimum element from the heap.
194+
*
195+
* @return HeapElement with the lowest key
196+
* @throws EmptyHeapException if the heap is empty
197+
*/
198+
private HeapElement extractMin() throws EmptyHeapException {
199+
if (minHeap.isEmpty()) {
200+
throw new EmptyHeapException("Cannot extract from empty heap");
201+
}
202+
HeapElement result = minHeap.getFirst();
203+
deleteElement(1);
84204
return result;
85205
}
86206

207+
/**
208+
* {@inheritDoc}
209+
*/
87210
@Override
88-
public final void insertElement(HeapElement element) {
211+
public void insertElement(HeapElement element) {
212+
if (element == null) {
213+
throw new IllegalArgumentException("Cannot insert null element");
214+
}
89215
minHeap.add(element);
90216
toggleUp(minHeap.size());
91217
}
92218

219+
/**
220+
* {@inheritDoc}
221+
*/
93222
@Override
94-
public void deleteElement(int elementIndex) {
223+
public void deleteElement(int elementIndex) throws EmptyHeapException {
95224
if (minHeap.isEmpty()) {
96-
try {
97-
throw new EmptyHeapException("Attempt to delete an element from an empty heap");
98-
} catch (EmptyHeapException e) {
99-
e.printStackTrace();
100-
}
225+
throw new EmptyHeapException("Cannot delete from empty heap");
101226
}
102227
if ((elementIndex > minHeap.size()) || (elementIndex <= 0)) {
103-
throw new IndexOutOfBoundsException("Index out of heap range");
104-
}
105-
// The last element in heap replaces the one to be deleted
106-
minHeap.set(elementIndex - 1, getElement(minHeap.size()));
107-
minHeap.remove(minHeap.size());
108-
// Shall the new element be moved up...
109-
if (getElementKey(elementIndex) < getElementKey((int) Math.floor(elementIndex / 2.0))) {
110-
toggleUp(elementIndex);
111-
} // ... or down ?
112-
else if (((2 * elementIndex <= minHeap.size()) && (getElementKey(elementIndex) > getElementKey(elementIndex * 2))) || ((2 * elementIndex < minHeap.size()) && (getElementKey(elementIndex) > getElementKey(elementIndex * 2)))) {
113-
toggleDown(elementIndex);
228+
throw new IndexOutOfBoundsException("Index " + elementIndex + " is out of heap range [1, " + minHeap.size() + "]");
229+
}
230+
231+
// Replace with last element and remove last position
232+
minHeap.set(elementIndex - 1, minHeap.getLast());
233+
minHeap.removeLast();
234+
235+
// No need to toggle if we just removed the last element
236+
if (!minHeap.isEmpty() && elementIndex <= minHeap.size()) {
237+
// Determine whether to toggle up or down
238+
if (elementIndex > 1 && getElementKey(elementIndex) < getElementKey((int) Math.floor(elementIndex / 2.0))) {
239+
toggleUp(elementIndex);
240+
} else {
241+
toggleDown(elementIndex);
242+
}
114243
}
115244
}
116245

246+
/**
247+
* {@inheritDoc}
248+
*/
117249
@Override
118250
public HeapElement getElement() throws EmptyHeapException {
119-
try {
120-
return extractMin();
121-
} catch (Exception e) {
122-
throw new EmptyHeapException("Heap is empty. Error retrieving element", e);
123-
}
251+
return extractMin();
252+
}
253+
254+
/**
255+
* Returns the current size of the heap.
256+
*
257+
* @return number of elements in the heap
258+
*/
259+
public int size() {
260+
return minHeap.size();
261+
}
262+
263+
/**
264+
* Checks if the heap is empty.
265+
*
266+
* @return true if the heap contains no elements
267+
*/
268+
public boolean isEmpty() {
269+
return minHeap.isEmpty();
124270
}
125271
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy