Skip to content

Commit cf4a672

Browse files
committed
Add fibonacci heap.
1 parent a3c5778 commit cf4a672

File tree

7 files changed

+328
-14
lines changed

7 files changed

+328
-14
lines changed

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ Very thanks for all of you interested in contributing!
66

77
We encourage you to contribute anything that makes this project a better one. You can think about the following questions:
88

9-
+ Is there any BUGs or bad implementations in the current code?
10-
+ Is the documents and comments not clearly enough?
9+
+ Are there any BUGs or bad implementations in the current code?
10+
+ Are the documents and comments not clear enough?
1111
+ An algorithm not found?
1212

1313
If yes, don't hesitate to be a contributor!

src/container/BTree.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2021 jingedawang
2+
* Copyright 2022 jingedawang
33
*/
44
package container;
55

src/container/FibonacciHeap.java

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
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+
}

src/container/Heap.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
* A heap is a data structure which could easily access and extract values from its top.
1010
*/
11-
public interface Heap {
11+
public interface Heap extends Container {
1212

1313
/**
1414
* Get the top value of the heap.

src/container/Node.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
*/
44
package container;
55

6+
import java.util.LinkedList;
7+
68
/**
79
* Node class for each element of search tree.
810
*
@@ -63,7 +65,7 @@ public Node(Color color) {
6365
}
6466

6567
///
66-
/// Fields for binary search tree and red-black tree.
68+
/// Fields for binary search tree.
6769
///
6870

6971
/**
@@ -86,6 +88,10 @@ public Node(Color color) {
8688
*/
8789
public Node right;
8890

91+
///
92+
/// Fields for red-black tree.
93+
///
94+
8995
/**
9096
* The color of the node, either red or black.
9197
*/
@@ -125,6 +131,27 @@ public Node(Color color) {
125131
*/
126132
public boolean isLeaf;
127133

134+
///
135+
/// Fields for fibonacci heap.
136+
///
137+
138+
/**
139+
* The number of the children of this node.
140+
*/
141+
public int degree;
142+
143+
/**
144+
* Flag indicates whether this node has lost children since it was placed to current position.
145+
*/
146+
public boolean marked;
147+
148+
/**
149+
* The children list of the node.
150+
*
151+
* In fibonacci heap, we use a linked list to accelerate the modification for children.
152+
*/
153+
public LinkedList<Node> childrenList;
154+
128155
/**
129156
* Clone this node.
130157
* <p>

test/ContainerTest.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ public class ContainerTest {
1616
void empty() {
1717
BinarySearchTree binarySearchTree = new BinarySearchTree();
1818
BTree bTree = new BTree();
19-
BinaryHeap heap = new BinaryHeap();
19+
BinaryHeap binaryHeap = new BinaryHeap();
20+
FibonacciHeap fibonacciHeap = new FibonacciHeap();
2021
PriorityQueue priorityQueue = new PriorityQueue();
2122
RedBlackTree redBlackTree = new RedBlackTree();
2223

2324
Assertions.assertTrue(binarySearchTree.empty());
2425
Assertions.assertTrue(bTree.empty());
25-
Assertions.assertTrue(heap.empty());
26+
Assertions.assertTrue(fibonacciHeap.empty());
27+
Assertions.assertTrue(binaryHeap.empty());
2628
Assertions.assertTrue(priorityQueue.empty());
2729
Assertions.assertTrue(redBlackTree.empty());
2830
}
@@ -32,13 +34,15 @@ void size() {
3234
int[] arr = ArrayGenerator.randomArray(30, 33);
3335
BinarySearchTree binarySearchTree = new BinarySearchTree(arr.clone());
3436
BTree bTree = new BTree(arr.clone());
35-
BinaryHeap heap = new BinaryHeap(arr.clone(), true);
37+
BinaryHeap binaryHeap = new BinaryHeap(arr.clone(), true);
38+
FibonacciHeap fibonacciHeap = new FibonacciHeap(arr.clone());
3639
PriorityQueue priorityQueue = new PriorityQueue(arr.clone());
3740
RedBlackTree redBlackTree = new RedBlackTree(arr.clone());
3841

3942
Assertions.assertEquals(arr.length, binarySearchTree.size());
4043
Assertions.assertEquals(arr.length, bTree.size());
41-
Assertions.assertEquals(arr.length, heap.size());
44+
Assertions.assertEquals(arr.length, binaryHeap.size());
45+
Assertions.assertEquals(arr.length, fibonacciHeap.size());
4246
Assertions.assertEquals(arr.length, priorityQueue.size());
4347
Assertions.assertEquals(arr.length, redBlackTree.size());
4448
}

0 commit comments

Comments
 (0)