1
+ /**
2
+ * Copyright 2022 jingedawang
3
+ */
4
+ package container ;
5
+
6
+ import utils .ArrayGenerator ;
7
+ import utils .ArrayPrinter ;
8
+
9
+ import java .util .HashMap ;
10
+ import java .util .LinkedList ;
11
+
12
+ /**
13
+ * FibonacciHeap is a kind of heap which is fast when adjusting a key.
14
+ *
15
+ * Specifically, when decrease the key of a node, Fibonacci heap can finish it in constant-time amortized complexity.
16
+ * It is really helpful in many graph algorithms who need to decrease the weight of the edges frequently.
17
+ */
18
+ public class FibonacciHeap implements Heap {
19
+
20
+ /**
21
+ * Demo code.
22
+ */
23
+ public static void main (String [] args ) {
24
+ int [] arr = ArrayGenerator .fixedArray ();
25
+ FibonacciHeap heap = new FibonacciHeap (arr );
26
+ System .out .println ("Array used to build fibonacci heap:" );
27
+ ArrayPrinter .print (arr );
28
+
29
+ System .out .println ("The top value of the fibonacci heap is:" );
30
+ System .out .println (heap .top ());
31
+
32
+ System .out .println ("Pop values from the fibonacci heap:" );
33
+ while (heap .size () > 1 ) {
34
+ System .out .print (heap .pop () + ", " );
35
+ }
36
+ System .out .println (heap .pop ());
37
+ }
38
+
39
+ /**
40
+ * Default constructor.
41
+ */
42
+ public FibonacciHeap () {
43
+ }
44
+
45
+ /**
46
+ * Construct a fibonacci heap with given array.
47
+ *
48
+ * @param arr The data used for constructing the fibonacci heap.
49
+ */
50
+ public FibonacciHeap (int [] arr ) {
51
+ for (int value : arr ) {
52
+ insert (new Node (value ));
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Get the top value of the heap.
58
+ *
59
+ * For fibonacci heap, the top value is the minimum value.
60
+ * @return The top value of the heap.
61
+ */
62
+ @ Override
63
+ public int top () {
64
+ return minimum .value ;
65
+ }
66
+
67
+ /**
68
+ * Get and remove the top value of the heap.
69
+ *
70
+ * @return The top value of the heap.
71
+ */
72
+ @ Override
73
+ public int pop () {
74
+ if (size <= 0 ) {
75
+ throw new ArrayIndexOutOfBoundsException ("Can not pop a value from an empty heap." );
76
+ }
77
+ for (Node child : minimum .childrenList )
78
+ {
79
+ rootList .add (child );
80
+ child .parent = null ;
81
+ }
82
+ rootList .remove (minimum );
83
+ int minimumValue = minimum .value ;
84
+ if (rootList .isEmpty ()) {
85
+ minimum = null ;
86
+ }
87
+ else {
88
+ minimum = rootList .getFirst ();
89
+ consolidate ();
90
+ }
91
+ size --;
92
+ return minimumValue ;
93
+ }
94
+
95
+ /**
96
+ * Insert a node into the heap.
97
+ *
98
+ * @param newNode The node to be inserted.
99
+ */
100
+ @ Override
101
+ public void insert (Node newNode ) {
102
+ newNode .degree = 0 ;
103
+ newNode .parent = null ;
104
+ newNode .childrenList = new LinkedList <>();
105
+ newNode .marked = false ;
106
+
107
+ // Directly insert the new node into root list.
108
+ // Postpone the consolidating procedure to pop method.
109
+ if (minimum == null ) {
110
+ rootList = new LinkedList <>();
111
+ rootList .add (newNode );
112
+ minimum = newNode ;
113
+ }
114
+ else {
115
+ rootList .add (newNode );
116
+ if (newNode .value < minimum .value ) {
117
+ minimum = newNode ;
118
+ }
119
+ }
120
+ size ++;
121
+ }
122
+
123
+ /**
124
+ * Delete a node from the heap.
125
+ *
126
+ * @param node The node to be deleted.
127
+ */
128
+ @ Override
129
+ public void delete (Node node ) {
130
+ decreaseValue (node , Integer .MIN_VALUE );
131
+ pop ();
132
+ }
133
+
134
+ /**
135
+ * Check if the heap has no elements.
136
+ *
137
+ * @return {@code true} if the heap has no elements, {@code false} otherwise.
138
+ */
139
+ @ Override
140
+ public boolean empty () {
141
+ return size == 0 ;
142
+ }
143
+
144
+ /**
145
+ * Get the size of the heap.
146
+ *
147
+ * @return The size of the heap.
148
+ */
149
+ @ Override
150
+ public int size () {
151
+ return size ;
152
+ }
153
+
154
+ /**
155
+ * Consolidate the root list of the fibonacci heap to make sure each root have different degrees.
156
+ *
157
+ * This can be achieved by recursively merging roots with the same degree.
158
+ */
159
+ private void consolidate () {
160
+ // Use a HashMap to track the node of each degree.
161
+ HashMap <Integer , Node > degreeMap = new HashMap <>((int )Math .log (size ));
162
+
163
+ // Since the iterator of LinkedList is fail-fast, we cannot modify it during traversing.
164
+ // Instead, we traverse another copy while modifying the original root list.
165
+ Node [] rootArr = rootList .toArray (new Node [0 ]);
166
+ for (Node node : rootArr ) {
167
+ int degree = node .degree ;
168
+ while (degreeMap .containsKey (degree )) {
169
+ Node sameDegreeNode = degreeMap .get (degree );
170
+ if (node .value > sameDegreeNode .value ) {
171
+ Node temp = node ;
172
+ node = sameDegreeNode ;
173
+ sameDegreeNode = temp ;
174
+ }
175
+ link (sameDegreeNode , node );
176
+ degreeMap .remove (degree );
177
+ degree ++;
178
+ }
179
+ degreeMap .put (degree , node );
180
+ }
181
+
182
+ // Reset the root list according to degreeMap.
183
+ minimum = null ;
184
+ rootList .clear ();
185
+ for (Node node : degreeMap .values ()) {
186
+ rootList .add (node );
187
+ if (minimum == null || node .value < minimum .value ) {
188
+ minimum = node ;
189
+ }
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Link the child node to the given parent node.
195
+ *
196
+ * @param child The node to be linked as a child of the parent node.
197
+ * @param parent The node where the child node to be linked to.
198
+ */
199
+ private void link (Node child , Node parent ) {
200
+ rootList .remove (child );
201
+ parent .childrenList .add (child );
202
+ child .parent = parent ;
203
+ parent .degree ++;
204
+ child .marked = false ;
205
+ }
206
+
207
+ /**
208
+ * Decrease the value of the specified node.
209
+ *
210
+ * @param node The node whose value will be decreased.
211
+ * @param newValue The new value for the node. It should be smaller than original value of the node.
212
+ */
213
+ private void decreaseValue (Node node , int newValue ) {
214
+ if (newValue > node .value ) {
215
+ throw new IllegalArgumentException ("New value should be smaller than current value." );
216
+ }
217
+ node .value = newValue ;
218
+ Node parent = node .parent ;
219
+ if (parent != null && node .value < parent .value ) {
220
+ // The new value violate the minimum heap order.
221
+ // Simply cut the node and move it root list.
222
+ // But still need to check if its parent also needs to be cut.
223
+ cut (node , parent );
224
+ cascadingCut (parent );
225
+ }
226
+ if (node .value < minimum .value ) {
227
+ minimum = node ;
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Cut the child from the parent node and move it to root list.
233
+ *
234
+ * @param child The node to be cut from its parent node.
235
+ * @param parent The node where the child to be cut from.
236
+ */
237
+ private void cut (Node child , Node parent ) {
238
+ parent .childrenList .remove (child );
239
+ parent .degree --;
240
+ rootList .add (child );
241
+ child .parent = null ;
242
+ child .marked = false ;
243
+ }
244
+
245
+ /**
246
+ * Cascading cut the node up to the root.
247
+ *
248
+ * @param node The node where the cascading cut started.
249
+ */
250
+ private void cascadingCut (Node node ) {
251
+ Node parent = node .parent ;
252
+ if (parent != null ) {
253
+ if (!node .marked ) {
254
+ // If the node is not marked, it hasn't lost child so far.
255
+ // Mark it to indicate it lost a child now.
256
+ node .marked = true ;
257
+ }
258
+ else {
259
+ // If the node is already marked, it means it lost a second child now.
260
+ // Cut it and continue check if its parent should be cut.
261
+ cut (node , parent );
262
+ cascadingCut (parent );
263
+ }
264
+ }
265
+ }
266
+
267
+ // The number of the nodes in this heap.
268
+ private int size ;
269
+
270
+ // The minimum node of this heap.
271
+ private Node minimum ;
272
+
273
+ // The root list of this fibonacci heap.
274
+ private LinkedList <Node > rootList ;
275
+
276
+ }
0 commit comments