Skip to content

Commit 17a9186

Browse files
✨ feat: First working draft.
1 parent ac6ae19 commit 17a9186

16 files changed

+8365
-7
lines changed

package.json

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
},
3232
"dependencies": {},
3333
"devDependencies": {
34+
"@aureooms/js-heap-spec": "^14.0.0",
3435
"@babel/cli": "7.8.4",
3536
"@babel/core": "7.9.0",
3637
"@babel/plugin-proposal-async-generator-functions": "7.8.3",
@@ -52,7 +53,12 @@
5253
"lib"
5354
],
5455
"homepage": "https://aureooms.github.io/js-fibonacci-heap",
55-
"keywords": ["data", "fibonacci", "heap", "structure"],
56+
"keywords": [
57+
"data",
58+
"fibonacci",
59+
"heap",
60+
"structure"
61+
],
5662
"license": "AGPL-3.0",
5763
"main": "lib/index.js",
5864
"repository": {
@@ -77,12 +83,8 @@
7783
"unicorn"
7884
],
7985
"rules": {
80-
"unicorn/filename-case": [
81-
"error",
82-
{
83-
"case": "camelCase"
84-
}
85-
]
86+
"camelcase": "off",
87+
"unicorn/filename-case": "off"
8688
}
8789
}
8890
}

src/FibonacciHeap.js

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
import Node from './Node';
2+
3+
import list_debug from './list_debug';
4+
import list_insert from './list_insert';
5+
import list_remove from './list_remove';
6+
import list_concatenate from './list_concatenate';
7+
import list_reset_parent from './list_reset_parent';
8+
9+
import consolidate from './consolidate';
10+
import cut from './cut';
11+
import cascading_cut from './cascading_cut';
12+
13+
/**
14+
* See CLRS09 Chapter 19 on the Fibonacci Heap.
15+
*/
16+
export default class FibonacciHeap {
17+
/**
18+
* Make-heap: creates a new empty heap.
19+
*
20+
* To make an empty Fibonacci heap, the MAKE-FIB-HEAP procedure
21+
* allocates and returns the Fibonacci heap object H, where H.n = 0 and
22+
* H.min = NIL ; there are no trees in H. Because t(H) = 0 and m(H) = 0,
23+
* the potential of the empty Fibonacci heap is pot(H) = 0. The amortized
24+
* cost of MAKE-FIB-HEAP is thus equal to its O(1) actual cost.
25+
*/
26+
constructor(compare) {
27+
//console.debug('constructor');
28+
this.compare = compare; // Comparison function
29+
/**
30+
* We access a given Fibonacci heap H by a pointer H.min to the root of
31+
* a tree containing the minimum key; we call this node the minimum
32+
* node of the Fibonacci heap. If more than one root has a key with the
33+
* minimum value, then any such root may serve as the minimum node.
34+
* When a Fibonacci heap H is empty, H.min is NIL.
35+
*/
36+
this.min = null;
37+
}
38+
39+
/**
40+
* Find-min: simply return the top element of the heap.
41+
*/
42+
head() {
43+
//console.debug('head');
44+
if (this.min === null) return undefined;
45+
return this.min.value;
46+
}
47+
48+
/**
49+
* Minimum: return a pointer to the element whose key is minimum
50+
*
51+
* The minimum node of a Fibonacci heap H is given by the pointer H.min, so
52+
* we can find the minimum node in O(1) actual time. Because the potential
53+
* of H does not change, the amortized cost of this operation is equal to
54+
* its O(1) actual cost.
55+
*/
56+
headreference() {
57+
//console.debug('headreference');
58+
return this.min;
59+
}
60+
61+
/**
62+
*/
63+
pop() {
64+
//console.debug('pop');
65+
const min = this.popreference();
66+
return min === null ? undefined : min.value;
67+
}
68+
69+
/**
70+
* FIB-HEAP-EXTRACT-MIN(H):
71+
*
72+
* Extracting the minimum node
73+
*
74+
* The process of extracting the minimum node is the most complicated of
75+
* the operations presented in this section. It is also where the delayed
76+
* work of consolidating trees in the root list finally occurs. The
77+
* following pseudocode extracts the minimum node. The code assumes for
78+
* convenience that when a node is removed from a linked list, pointers
79+
* remaining in the list are updated, but pointers in the extracted node
80+
* are left unchanged. It also calls the auxiliary procedure CONSOLIDATE,
81+
* which we shall see shortly.
82+
*/
83+
popreference() {
84+
//console.debug('popreference');
85+
const z = this.min;
86+
if (z === null) return null;
87+
//console.debug('z is', z.value);
88+
list_debug(z.next, 'z list');
89+
if (z.children !== null) {
90+
list_debug(z.children, 'z children');
91+
list_reset_parent(z.children);
92+
list_concatenate(z, z.children);
93+
list_debug(z.next, 'z after concatenation with its children');
94+
}
95+
96+
if (z === z.next) this.min = null;
97+
else {
98+
list_remove(z);
99+
list_debug(z.next, 'z after removal');
100+
this.min = consolidate(this.compare, z.next);
101+
}
102+
103+
z.next = z; // TODO remove this
104+
z.prev = z;
105+
z.children = null;
106+
z.degree = 0;
107+
z.mark = false;
108+
//console.debug('popped', z.value);
109+
return z;
110+
}
111+
112+
/**
113+
* Create a new node for the inserted element and put it into the heap.
114+
*/
115+
push(value) {
116+
//console.debug('push', value);
117+
const node = new Node(value);
118+
return this.pushreference(node);
119+
}
120+
121+
/**
122+
* Insert: insert new node.
123+
* /!\ ref.next = ref.prev = ref which means all references that are
124+
* external to the tree must reset .next and .prev and one must not call
125+
* FibonacciHeap#pushreference with an internal reference from this tree or
126+
* another, except the root of another tree.
127+
*
128+
* Change in potential is 1. Therefore amortized cost is O(1).
129+
*/
130+
pushreference(ref) {
131+
//console.debug('pushreference');
132+
if (this.min === null) {
133+
// Create a root list for H containing just x (by precondition)
134+
this.min = ref;
135+
} else {
136+
// This.min != null != ref
137+
// Insert x into H's root list
138+
list_insert(this.min, ref);
139+
// Update minimum
140+
if (this.compare(ref.value, this.min.value) < 0) {
141+
this.min = ref;
142+
}
143+
}
144+
145+
return ref;
146+
}
147+
148+
/**
149+
* Union: Uniting two Fibonacci heaps
150+
*
151+
* The following procedure unites Fibonacci heaps H_1 and H_2, destroying
152+
* H_1 and H_2 in the process. It simply concatenates the root lists of H_1
153+
* and H_2 and then determines the new minimum node. Afterward, the objects
154+
* representing H_1 and H_2 will never be used again.
155+
*
156+
* /!\ Assumes the same comparison function is used in both trees.
157+
*
158+
* Change in potential is zero. Amortized cost is O(1), the actual cost.
159+
*/
160+
meld(other) {
161+
//console.debug('meld');
162+
const ref = other.min;
163+
if (ref === null) return;
164+
if (this.min === null) this.min = ref;
165+
else {
166+
// This.min != null != ref
167+
// Concatenate the root list of H_2 with the root list of H
168+
list_concatenate(this.min, ref);
169+
// Update minimum
170+
if (this.compare(ref.value, this.min.value) < 0) {
171+
this.min = ref;
172+
}
173+
}
174+
}
175+
176+
/**
177+
* Synonym for FibonacciHeap#meld.
178+
* TODO Remove this.
179+
*/
180+
merge(other) {
181+
this.meld(other);
182+
}
183+
184+
/**
185+
* @param {Node} ref Non-null internal node object.
186+
* @param {Object} value The new value for ref.
187+
*/
188+
update(ref, value) {
189+
//console.debug('update');
190+
const d = this.compare(value, ref.value);
191+
192+
if (d < 0) this.decreasekey(ref, value);
193+
else if (d > 0) this.increasekey(ref, value);
194+
else ref.value = value;
195+
}
196+
197+
/**
198+
* FIB-HEAP-DECREASE-kEY:
199+
* @param {Node} ref Non-null internal node object.
200+
* @param {Object} value The new value for ref.
201+
*/
202+
decreasekey(ref, value) {
203+
//console.debug('decreasekey');
204+
ref.value = value;
205+
if (ref !== this.min) {
206+
// This.min != null, ref != null
207+
const y = ref.parent;
208+
if (y !== null && this.compare(ref.value, y.value) < 0) {
209+
cut(ref, y);
210+
list_insert(this.min, ref);
211+
for (const x of cascading_cut(y)) list_insert(this.min, x);
212+
}
213+
214+
if (this.compare(ref.value, this.min.value) < 0) {
215+
this.min = ref;
216+
}
217+
}
218+
}
219+
220+
/**
221+
* Increase-key: remove the item at the key to be increased, replace
222+
* the key with a larger key, then push the result back into the heap.
223+
*
224+
* @param {Node} ref Non-null internal node object.
225+
* @param {Object} value The new value for ref.
226+
*
227+
*/
228+
increasekey(ref, value) {
229+
//console.debug('increasekey');
230+
this.delete(ref);
231+
232+
ref.value = value;
233+
234+
this.pushreference(ref);
235+
}
236+
237+
/**
238+
* Delete
239+
* ref must be internal
240+
* ref.prev and ref.next get reset to null
241+
*/
242+
delete(ref) {
243+
//console.debug('>>>>')
244+
//console.debug('DELETE', ref.value);
245+
if (ref !== this.min) {
246+
// This.min != null, ref != null
247+
const y = ref.parent;
248+
if (y !== null) {
249+
cut(ref, y);
250+
list_insert(this.min, ref);
251+
for (const x of cascading_cut(y)) list_insert(this.min, x);
252+
}
253+
254+
this.min = ref;
255+
}
256+
257+
this.popreference();
258+
}
259+
}

src/Node.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
export default class Node {
2+
constructor(value) {
3+
this.value = value; // Key
4+
/**
5+
* As Figure 19.2(b) shows, each node x contains a pointer x.p to its
6+
* parent and a pointer x.child to any one of its children. The
7+
* children of x are linked together in a circular, doubly linked list,
8+
* which we call the child list of x. Each child y in a child list has
9+
* pointers y.left and y.right that point to y’s left and right
10+
* siblings, respectively. If node y is an only child, then y.left =
11+
* y.right = y. Siblings may appear in a child list in any order.
12+
*/
13+
this.parent = null;
14+
this.prev = this; // Pointer to previous (left) sibling
15+
this.next = this; // Pointer to next (right) sibling
16+
this.children = null; // Pointer to some child
17+
/**
18+
* Each node has two other attributes. We store the number of children
19+
* in the child list of node x in x.degree.
20+
*/
21+
this.degree = 0; // The number of children
22+
/**
23+
* The boolean-valued attribute x.mark indicates whether
24+
* node x has lost a child since the last time x was made the child of another node.
25+
* Newly created nodes are unmarked, and a node x becomes unmarked whenever it
26+
* is made the child of another node. Until we look at the DECREASE-KEY operation
27+
* in Section 19.3, we will just set all mark attributes to FALSE .
28+
*/
29+
this.mark = false; // Newly created nodes are unmarked
30+
}
31+
}

src/cascading_cut.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import cut from './cut';
2+
3+
export default function* cascading_cut(y) {
4+
//console.debug('cascading_cut');
5+
while (true) {
6+
const z = y.parent;
7+
if (z === null) break;
8+
if (!y.mark) {
9+
y.mark = true;
10+
break;
11+
}
12+
13+
cut(y, z);
14+
yield y;
15+
y = z;
16+
}
17+
}

0 commit comments

Comments
 (0)