4
4
import java .util .List ;
5
5
6
6
/**
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
+ * ```java
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
+ * ```
9
26
*
10
27
* @author Nicolas Renard
28
+ * @author [Your name] (documentation improvements)
11
29
*/
12
30
public class MaxHeap implements Heap {
13
31
32
+ /** The internal list that stores heap elements */
14
33
private final List <HeapElement > maxHeap ;
15
34
35
+ /**
36
+ * Constructs a new MaxHeap from a list of elements.
37
+ * Null elements in the input list are ignored with a warning message.
38
+ *
39
+ * @param listElements List of HeapElement objects to initialize the heap
40
+ * @throws IllegalArgumentException if the input list is null
41
+ */
16
42
public MaxHeap (List <HeapElement > listElements ) {
43
+ if (listElements == null ) {
44
+ throw new IllegalArgumentException ("Input list cannot be null" );
45
+ }
46
+
17
47
maxHeap = new ArrayList <>();
18
48
for (HeapElement heapElement : listElements ) {
19
49
if (heapElement != null ) {
@@ -28,104 +58,162 @@ public MaxHeap(List<HeapElement> listElements) {
28
58
}
29
59
30
60
/**
31
- * Get the element at a given index. The key for the list is equal to index
32
- * value - 1
61
+ * Retrieves the element at the specified index without removing it.
62
+ * Note: The index is 1-based for consistency with heap operations.
33
63
*
34
- * @param elementIndex index
35
- * @return heapElement
64
+ * @param elementIndex 1-based index of the element to retrieve
65
+ * @return HeapElement at the specified index
66
+ * @throws IndexOutOfBoundsException if the index is invalid
36
67
*/
37
68
public HeapElement getElement (int elementIndex ) {
38
69
if ((elementIndex <= 0 ) || (elementIndex > maxHeap .size ())) {
39
- throw new IndexOutOfBoundsException ("Index out of heap range" );
70
+ throw new IndexOutOfBoundsException ("Index " + elementIndex + " is out of heap range [1, " + maxHeap . size () + "] " );
40
71
}
41
72
return maxHeap .get (elementIndex - 1 );
42
73
}
43
74
44
- // Get the key of the element at a given index
75
+ /**
76
+ * Retrieves the key value of an element at the specified index.
77
+ *
78
+ * @param elementIndex 1-based index of the element
79
+ * @return double value representing the key
80
+ * @throws IndexOutOfBoundsException if the index is invalid
81
+ */
45
82
private double getElementKey (int elementIndex ) {
46
83
if ((elementIndex <= 0 ) || (elementIndex > maxHeap .size ())) {
47
- throw new IndexOutOfBoundsException ("Index out of heap range" );
84
+ throw new IndexOutOfBoundsException ("Index " + elementIndex + " is out of heap range [1, " + maxHeap . size () + "] " );
48
85
}
49
-
50
86
return maxHeap .get (elementIndex - 1 ).getKey ();
51
87
}
52
88
53
- // Swaps two elements in the heap
89
+ /**
90
+ * Swaps two elements in the heap.
91
+ *
92
+ * @param index1 1-based index of first element
93
+ * @param index2 1-based index of second element
94
+ */
54
95
private void swap (int index1 , int index2 ) {
55
96
HeapElement temporaryElement = maxHeap .get (index1 - 1 );
56
97
maxHeap .set (index1 - 1 , maxHeap .get (index2 - 1 ));
57
98
maxHeap .set (index2 - 1 , temporaryElement );
58
99
}
59
100
60
- // Toggle an element up to its right place as long as its key is lower than its parent's
101
+ /**
102
+ * Moves an element up the heap until heap properties are satisfied.
103
+ * This operation is called after insertion to maintain heap properties.
104
+ *
105
+ * @param elementIndex 1-based index of the element to move up
106
+ */
61
107
private void toggleUp (int elementIndex ) {
62
108
double key = maxHeap .get (elementIndex - 1 ).getKey ();
63
- while (getElementKey ((int ) Math .floor (elementIndex / 2.0 )) < key ) {
109
+ while (elementIndex > 1 && getElementKey ((int ) Math .floor (elementIndex / 2.0 )) < key ) {
64
110
swap (elementIndex , (int ) Math .floor (elementIndex / 2.0 ));
65
111
elementIndex = (int ) Math .floor (elementIndex / 2.0 );
66
112
}
67
113
}
68
114
69
- // Toggle an element down to its right place as long as its key is higher
70
- // than any of its children's
115
+ /**
116
+ * Moves an element down the heap until heap properties are satisfied.
117
+ * This operation is called after deletion to maintain heap properties.
118
+ *
119
+ * @param elementIndex 1-based index of the element to move down
120
+ */
71
121
private void toggleDown (int elementIndex ) {
72
122
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 ;
123
+ boolean wrongOrder = (2 * elementIndex <= maxHeap . size () && key < getElementKey (elementIndex * 2 )) || (2 * elementIndex + 1 <= maxHeap . size () && key < getElementKey (elementIndex * 2 + 1 ));
124
+
125
+ while ( 2 * elementIndex <= maxHeap . size () && wrongOrder ) {
126
+ int largerChildIndex ;
127
+ if ( 2 * elementIndex + 1 <= maxHeap . size () && getElementKey ( elementIndex * 2 + 1 ) > getElementKey ( elementIndex * 2 )) {
128
+ largerChildIndex = 2 * elementIndex + 1 ;
79
129
} else {
80
- swap (elementIndex , 2 * elementIndex );
81
- elementIndex = 2 * elementIndex ;
130
+ largerChildIndex = 2 * elementIndex ;
82
131
}
83
- wrongOrder = (key < getElementKey (elementIndex * 2 )) || (key < getElementKey (Math .min (elementIndex * 2 , maxHeap .size ())));
132
+
133
+ swap (elementIndex , largerChildIndex );
134
+ elementIndex = largerChildIndex ;
135
+
136
+ wrongOrder = (2 * elementIndex <= maxHeap .size () && key < getElementKey (elementIndex * 2 )) || (2 * elementIndex + 1 <= maxHeap .size () && key < getElementKey (elementIndex * 2 + 1 ));
84
137
}
85
138
}
86
139
87
- private HeapElement extractMax () {
140
+ /**
141
+ * Extracts and returns the maximum element from the heap.
142
+ *
143
+ * @return HeapElement with the highest key
144
+ * @throws EmptyHeapException if the heap is empty
145
+ */
146
+ private HeapElement extractMax () throws EmptyHeapException {
147
+ if (maxHeap .isEmpty ()) {
148
+ throw new EmptyHeapException ("Cannot extract from empty heap" );
149
+ }
88
150
HeapElement result = maxHeap .get (0 );
89
- deleteElement (0 );
151
+ deleteElement (1 );
90
152
return result ;
91
153
}
92
154
155
+ /**
156
+ * {@inheritDoc}
157
+ */
93
158
@ Override
94
- public final void insertElement (HeapElement element ) {
159
+ public void insertElement (HeapElement element ) {
160
+ if (element == null ) {
161
+ throw new IllegalArgumentException ("Cannot insert null element" );
162
+ }
95
163
maxHeap .add (element );
96
164
toggleUp (maxHeap .size ());
97
165
}
98
166
167
+ /**
168
+ * {@inheritDoc}
169
+ */
99
170
@ Override
100
- public void deleteElement (int elementIndex ) {
171
+ public void deleteElement (int elementIndex ) throws EmptyHeapException {
101
172
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
- }
173
+ throw new EmptyHeapException ("Cannot delete from empty heap" );
107
174
}
108
175
if ((elementIndex > maxHeap .size ()) || (elementIndex <= 0 )) {
109
- throw new IndexOutOfBoundsException ("Index out of heap range" );
176
+ throw new IndexOutOfBoundsException ("Index " + elementIndex + " is out of heap range [1, " + maxHeap . size () + "] " );
110
177
}
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 );
178
+
179
+ // Replace with last element and remove last position
180
+ maxHeap .set (elementIndex - 1 , maxHeap .get (maxHeap .size () - 1 ));
181
+ maxHeap .remove (maxHeap .size () - 1 );
182
+
183
+ // No need to toggle if we just removed the last element
184
+ if (!maxHeap .isEmpty () && elementIndex <= maxHeap .size ()) {
185
+ // Determine whether to toggle up or down
186
+ if (elementIndex > 1 && getElementKey (elementIndex ) > getElementKey ((int ) Math .floor (elementIndex / 2.0 ))) {
187
+ toggleUp (elementIndex );
188
+ } else {
189
+ toggleDown (elementIndex );
190
+ }
120
191
}
121
192
}
122
193
194
+ /**
195
+ * {@inheritDoc}
196
+ */
123
197
@ Override
124
198
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
- }
199
+ return extractMax ();
200
+ }
201
+
202
+ /**
203
+ * Returns the current size of the heap.
204
+ *
205
+ * @return number of elements in the heap
206
+ */
207
+ public int size () {
208
+ return maxHeap .size ();
209
+ }
210
+
211
+ /**
212
+ * Checks if the heap is empty.
213
+ *
214
+ * @return true if the heap contains no elements
215
+ */
216
+ public boolean isEmpty () {
217
+ return maxHeap .isEmpty ();
130
218
}
131
219
}
0 commit comments