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