├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── package.json └── test ├── clear-test.js ├── custom-compare-test.js ├── decrease-key-test.js ├── delete-test.js ├── extract-min-test.js ├── fibonacci-heap-decrease-key-test.js ├── fibonacci-heap-delete-test.js ├── fibonacci-heap-extract-min-test.js ├── find-minimum-test.js ├── insert-test.js ├── is-empty-test.js ├── size-test.js └── union-test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .nyc_output 2 | coverage 3 | node_modules 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - 'stable' 5 | - '4' 6 | - '0.12' 7 | after_success: npm run coveralls 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Daniel Imms, http://www.growingwiththeweb.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # js-fibonacci-heap 2 | 3 | [![Build Status](https://api.travis-ci.org/gwtw/js-fibonacci-heap.svg?branch=master)](http://travis-ci.org/gwtw/js-fibonacci-heap) 4 | [![Coverage Status](https://coveralls.io/repos/github/gwtw/js-fibonacci-heap/badge.svg?branch=master)](https://coveralls.io/github/gwtw/js-fibonacci-heap?branch=master) 5 | 6 | A JavaScript implementation of the [Fibonacci heap](http://www.growingwiththeweb.com/data-structures/fibonacci-heap/overview/) data structure. 7 | 8 | ![](http://www.growingwiththeweb.com/images/data-structures/fibonacci-heap/fibonacci-heap.svg) 9 | 10 | ## Features 11 | 12 | - 100% test coverage 13 | - Supports all common heap operations 14 | - Store keys with optional associated values 15 | - Optional custom compare function that can utilize both key and value to give full control over the order of the data 16 | 17 | ## Install 18 | 19 | ```bash 20 | npm install --save @tyriar/fibonacci-heap 21 | ``` 22 | 23 | ## Usage 24 | 25 | ```javascript 26 | // Import npm module 27 | var FibonacciHeap = require('@tyriar/fibonacci-heap'); 28 | 29 | // Construct FibonacciHeap 30 | var heap = new FibonacciHeap(); 31 | // Insert keys only 32 | heap.insert(3); 33 | heap.insert(7); 34 | // Insert keys and values 35 | heap.insert(8, {foo: 'bar'}); 36 | heap.insert(1, {foo: 'baz'}); 37 | 38 | // Extract all nodes in order 39 | while (!heap.isEmpty()) { 40 | var node = heap.extractMinimum(); 41 | console.log('key: ' + node.key + ', value: ' + node.value); 42 | } 43 | // > key: 1, value: [object Object] 44 | // > key: 3, value: undefined 45 | // > key: 7, value: undefined 46 | // > key: 8, value: [object Object] 47 | 48 | // Construct custom compare FibonacciHeap 49 | heap = new FibonacciHeap(function (a, b) { 50 | return (a.key + a.value).localeCompare(b.key + b.value); 51 | }); 52 | heap.insert('2', 'B'); 53 | heap.insert('1', 'a'); 54 | heap.insert('1', 'A'); 55 | heap.insert('2', 'b'); 56 | 57 | // Extract all nodes in order 58 | while (!heap.isEmpty()) { 59 | var node = heap.extractMinimum(); 60 | console.log('key: ' + node.key + ', value: ' + node.value); 61 | } 62 | // > key: 1, value: a 63 | // > key: 1, value: A 64 | // > key: 2, value: b 65 | // > key: 2, value: B 66 | ``` 67 | 68 | ## Operation time complexity 69 | 70 | | Operation | Complexity | 71 | | -------------- | ---------- | 72 | | clear | Θ(1)\* | 73 | | decreaseKey | Θ(1)\* | 74 | | delete | O(log n)\* | 75 | | extractMinimum | O(log n)\* | 76 | | findMinimum | Θ(1) | 77 | | insert | Θ(1) | 78 | | isEmpty | Θ(1) | 79 | | size | Θ(n) | 80 | | union | Θ(1) | 81 | 82 | \* amortized 83 | 84 | ## API 85 | 86 | ### FibonacciHeap 87 | 88 | Creates a Fibonacci heap. 89 | 90 | **Parameters** 91 | 92 | - `customCompare` **function** An optional custom node comparison 93 | function. 94 | 95 | #### clear 96 | 97 | Clears the heap's data, making it an empty heap. 98 | 99 | #### decreaseKey 100 | 101 | Decreases a key of a node. 102 | 103 | **Parameters** 104 | 105 | - `node` **Node** The node to decrease the key of. 106 | - `newKey` **Object** The new key to assign to the node. 107 | 108 | #### delete 109 | 110 | Deletes a node. 111 | 112 | **Parameters** 113 | 114 | - `node` **Node** The node to delete. 115 | 116 | #### extractMinimum 117 | 118 | Extracts and returns the minimum node from the heap. 119 | 120 | Returns **Node** node The heap's minimum node or undefined if the heap is 121 | empty. 122 | 123 | #### findMinimum 124 | 125 | Returns the minimum node from the heap. 126 | 127 | Returns **Node** node The heap's minimum node or undefined if the heap is 128 | empty. 129 | 130 | #### insert 131 | 132 | Inserts a new key-value pair into the heap. 133 | 134 | **Parameters** 135 | 136 | - `key` **Object** The key to insert. 137 | - `value` **Object** The value to insert. 138 | 139 | Returns **Node** node The inserted node. 140 | 141 | #### isEmpty 142 | 143 | Returns **boolean** Whether the heap is empty. 144 | 145 | #### size 146 | 147 | Returns **number** The size of the heap. 148 | 149 | #### union 150 | 151 | Joins another heap to this heap. 152 | 153 | **Parameters** 154 | 155 | - `otherHeap` **BinaryHeap** The other heap. 156 | - `other` 157 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Creates a Fibonacci heap. 5 | * 6 | * @constructor 7 | * @param {function} customCompare An optional custom node comparison 8 | * function. 9 | */ 10 | var FibonacciHeap = function (customCompare) { 11 | this.minNode = undefined; 12 | this.nodeCount = 0; 13 | 14 | if (customCompare) { 15 | this.compare = customCompare; 16 | } 17 | }; 18 | 19 | /** 20 | * Clears the heap's data, making it an empty heap. 21 | */ 22 | FibonacciHeap.prototype.clear = function () { 23 | this.minNode = undefined; 24 | this.nodeCount = 0; 25 | }; 26 | 27 | /** 28 | * Decreases a key of a node. 29 | * 30 | * @param {Node} node The node to decrease the key of. 31 | * @param {Object} newKey The new key to assign to the node. 32 | */ 33 | FibonacciHeap.prototype.decreaseKey = function (node, newKey) { 34 | if (typeof node === 'undefined') { 35 | throw new Error('Cannot decrease key of non-existent node'); 36 | } 37 | if (this.compare({key: newKey}, {key: node.key}) > 0) { 38 | throw new Error('New key is larger than old key'); 39 | } 40 | 41 | node.key = newKey; 42 | var parent = node.parent; 43 | if (parent && this.compare(node, parent) < 0) { 44 | cut(node, parent, this.minNode, this.compare); 45 | cascadingCut(parent, this.minNode, this.compare); 46 | } 47 | if (this.compare(node, this.minNode) < 0) { 48 | this.minNode = node; 49 | } 50 | }; 51 | 52 | /** 53 | * Deletes a node. 54 | * 55 | * @param {Node} node The node to delete. 56 | */ 57 | FibonacciHeap.prototype.delete = function (node) { 58 | // This is a special implementation of decreaseKey that sets the argument to 59 | // the minimum value. This is necessary to make generic keys work, since there 60 | // is no MIN_VALUE constant for generic types. 61 | var parent = node.parent; 62 | if (parent) { 63 | cut(node, parent, this.minNode, this.compare); 64 | cascadingCut(parent, this.minNode, this.compare); 65 | } 66 | this.minNode = node; 67 | 68 | this.extractMinimum(); 69 | }; 70 | 71 | /** 72 | * Extracts and returns the minimum node from the heap. 73 | * 74 | * @return {Node} node The heap's minimum node or undefined if the heap is 75 | * empty. 76 | */ 77 | FibonacciHeap.prototype.extractMinimum = function () { 78 | var extractedMin = this.minNode; 79 | if (extractedMin) { 80 | // Set parent to undefined for the minimum's children 81 | if (extractedMin.child) { 82 | var child = extractedMin.child; 83 | do { 84 | child.parent = undefined; 85 | child = child.next; 86 | } while (child !== extractedMin.child); 87 | } 88 | 89 | var nextInRootList; 90 | if (extractedMin.next !== extractedMin) { 91 | nextInRootList = extractedMin.next; 92 | } 93 | // Remove min from root list 94 | removeNodeFromList(extractedMin); 95 | this.nodeCount--; 96 | 97 | // Merge the children of the minimum node with the root list 98 | this.minNode = mergeLists(nextInRootList, extractedMin.child, this.compare); 99 | if (this.minNode) { 100 | this.minNode = consolidate(this.minNode, this.compare); 101 | } 102 | } 103 | return extractedMin; 104 | }; 105 | 106 | /** 107 | * Returns the minimum node from the heap. 108 | * 109 | * @return {Node} node The heap's minimum node or undefined if the heap is 110 | * empty. 111 | */ 112 | FibonacciHeap.prototype.findMinimum = function () { 113 | return this.minNode; 114 | }; 115 | 116 | /** 117 | * Inserts a new key-value pair into the heap. 118 | * 119 | * @param {Object} key The key to insert. 120 | * @param {Object} value The value to insert. 121 | * @return {Node} node The inserted node. 122 | */ 123 | FibonacciHeap.prototype.insert = function (key, value) { 124 | var node = new Node(key, value); 125 | this.minNode = mergeLists(this.minNode, node, this.compare); 126 | this.nodeCount++; 127 | return node; 128 | }; 129 | 130 | /** 131 | * @return {boolean} Whether the heap is empty. 132 | */ 133 | FibonacciHeap.prototype.isEmpty = function () { 134 | return this.minNode === undefined; 135 | }; 136 | 137 | /** 138 | * @return {number} The size of the heap. 139 | */ 140 | FibonacciHeap.prototype.size = function () { 141 | if (this.isEmpty()) { 142 | return 0; 143 | } 144 | return getNodeListSize(this.minNode); 145 | }; 146 | 147 | /** 148 | * Joins another heap to this heap. 149 | * 150 | * @param {FibonacciHeap} other The other heap. 151 | */ 152 | FibonacciHeap.prototype.union = function (other) { 153 | this.minNode = mergeLists(this.minNode, other.minNode, this.compare); 154 | this.nodeCount += other.nodeCount; 155 | }; 156 | 157 | /** 158 | * Compares two nodes with each other. 159 | * 160 | * @private 161 | * @param {Object} a The first key to compare. 162 | * @param {Object} b The second key to compare. 163 | * @return {number} -1, 0 or 1 if a < b, a == b or a > b respectively. 164 | */ 165 | FibonacciHeap.prototype.compare = function (a, b) { 166 | if (a.key > b.key) { 167 | return 1; 168 | } 169 | if (a.key < b.key) { 170 | return -1; 171 | } 172 | return 0; 173 | }; 174 | 175 | /** 176 | * Creates an Iterator used to simplify the consolidate() method. It works by 177 | * making a shallow copy of the nodes in the root list and iterating over the 178 | * shallow copy instead of the source as the source will be modified. 179 | * 180 | * @private 181 | * @param {Node} start A node from the root list. 182 | */ 183 | var NodeListIterator = function (start) { 184 | this.index = -1; 185 | this.items = []; 186 | var current = start; 187 | do { 188 | this.items.push(current); 189 | current = current.next; 190 | } while (start !== current); 191 | }; 192 | 193 | /** 194 | * @return {boolean} Whether there is a next node in the iterator. 195 | */ 196 | NodeListIterator.prototype.hasNext = function () { 197 | return this.index < this.items.length - 1; 198 | }; 199 | 200 | /** 201 | * @return {Node} The next node. 202 | */ 203 | NodeListIterator.prototype.next = function () { 204 | return this.items[++this.index]; 205 | }; 206 | 207 | /** 208 | * Cut the link between a node and its parent, moving the node to the root list. 209 | * 210 | * @private 211 | * @param {Node} node The node being cut. 212 | * @param {Node} parent The parent of the node being cut. 213 | * @param {Node} minNode The minimum node in the root list. 214 | * @param {function} compare The node comparison function to use. 215 | * @return {Node} The heap's new minimum node. 216 | */ 217 | function cut(node, parent, minNode, compare) { 218 | node.parent = undefined; 219 | parent.degree--; 220 | if (node.next === node) { 221 | parent.child = undefined; 222 | } else { 223 | parent.child = node.next; 224 | } 225 | removeNodeFromList(node); 226 | minNode = mergeLists(minNode, node, compare); 227 | node.isMarked = false; 228 | return minNode; 229 | } 230 | 231 | /** 232 | * Perform a cascading cut on a node; mark the node if it is not marked, 233 | * otherwise cut the node and perform a cascading cut on its parent. 234 | * 235 | * @private 236 | * @param {Node} node The node being considered to be cut. 237 | * @param {Node} minNode The minimum node in the root list. 238 | * @param {function} compare The node comparison function to use. 239 | * @return {Node} The heap's new minimum node. 240 | */ 241 | function cascadingCut(node, minNode, compare) { 242 | var parent = node.parent; 243 | if (parent) { 244 | if (node.isMarked) { 245 | minNode = cut(node, parent, minNode, compare); 246 | minNode = cascadingCut(parent, minNode, compare); 247 | } else { 248 | node.isMarked = true; 249 | } 250 | } 251 | return minNode; 252 | } 253 | 254 | /** 255 | * Merge all trees of the same order together until there are no two trees of 256 | * the same order. 257 | * 258 | * @private 259 | * @param {Node} minNode The current minimum node. 260 | * @param {function} compare The node comparison function to use. 261 | * @return {Node} The new minimum node. 262 | */ 263 | function consolidate(minNode, compare) { 264 | var aux = []; 265 | var it = new NodeListIterator(minNode); 266 | while (it.hasNext()) { 267 | var current = it.next(); 268 | 269 | // If there exists another node with the same degree, merge them 270 | while (aux[current.degree]) { 271 | if (compare(current, aux[current.degree]) > 0) { 272 | var temp = current; 273 | current = aux[current.degree]; 274 | aux[current.degree] = temp; 275 | } 276 | linkHeaps(aux[current.degree], current, compare); 277 | aux[current.degree] = undefined; 278 | current.degree++; 279 | } 280 | 281 | aux[current.degree] = current; 282 | } 283 | 284 | minNode = undefined; 285 | for (var i = 0; i < aux.length; i++) { 286 | if (aux[i]) { 287 | // Remove siblings before merging 288 | aux[i].next = aux[i]; 289 | aux[i].prev = aux[i]; 290 | minNode = mergeLists(minNode, aux[i], compare); 291 | } 292 | } 293 | return minNode; 294 | } 295 | 296 | /** 297 | * Removes a node from a node list. 298 | * 299 | * @private 300 | * @param {Node} node The node to remove. 301 | */ 302 | function removeNodeFromList(node) { 303 | var prev = node.prev; 304 | var next = node.next; 305 | prev.next = next; 306 | next.prev = prev; 307 | node.next = node; 308 | node.prev = node; 309 | } 310 | 311 | /** 312 | * Links two heaps of the same order together. 313 | * 314 | * @private 315 | * @param {Node} max The heap with the larger root. 316 | * @param {Node} min The heap with the smaller root. 317 | * @param {function} compare The node comparison function to use. 318 | */ 319 | function linkHeaps(max, min, compare) { 320 | removeNodeFromList(max); 321 | min.child = mergeLists(max, min.child, compare); 322 | max.parent = min; 323 | max.isMarked = false; 324 | } 325 | 326 | /** 327 | * Merge two lists of nodes together. 328 | * 329 | * @private 330 | * @param {Node} a The first list to merge. 331 | * @param {Node} b The second list to merge. 332 | * @param {function} compare The node comparison function to use. 333 | * @return {Node} The new minimum node from the two lists. 334 | */ 335 | function mergeLists(a, b, compare) { 336 | if (!a && !b) { 337 | return undefined; 338 | } 339 | if (!a) { 340 | return b; 341 | } 342 | if (!b) { 343 | return a; 344 | } 345 | 346 | var temp = a.next; 347 | a.next = b.next; 348 | a.next.prev = a; 349 | b.next = temp; 350 | b.next.prev = b; 351 | 352 | return compare(a, b) < 0 ? a : b; 353 | } 354 | 355 | /** 356 | * Gets the size of a node list. 357 | * 358 | * @private 359 | * @param {Node} node A node within the node list. 360 | * @return {number} The size of the node list. 361 | */ 362 | function getNodeListSize(node) { 363 | var count = 0; 364 | var current = node; 365 | 366 | do { 367 | count++; 368 | if (current.child) { 369 | count += getNodeListSize(current.child); 370 | } 371 | current = current.next; 372 | } while (current !== node); 373 | 374 | return count; 375 | } 376 | 377 | /** 378 | * Creates a FibonacciHeap node. 379 | * 380 | * @constructor 381 | * @private 382 | */ 383 | function Node(key, value) { 384 | this.key = key; 385 | this.value = value; 386 | this.prev = this; 387 | this.next = this; 388 | this.degree = 0; 389 | 390 | this.parent = undefined; 391 | this.child = undefined; 392 | this.isMarked = undefined; 393 | } 394 | 395 | module.exports = FibonacciHeap; 396 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tyriar/fibonacci-heap", 3 | "version": "1.0.10", 4 | "description": "An implementation of the Fibonacci heap data structure", 5 | "scripts": { 6 | "doc": "documentation-readme -s \"API\"", 7 | "test": "xo && nyc ava", 8 | "coveralls": "nyc report --reporter=text-lcov | coveralls" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/gwtw/js-fibonacci-heap.git" 13 | }, 14 | "keywords": [ 15 | "A*", 16 | "computer science", 17 | "data structure", 18 | "dijkstra", 19 | "heap", 20 | "priority queue", 21 | "tree" 22 | ], 23 | "main": "index.js", 24 | "author": "Daniel Imms (http://www.growingwiththeweb.com)", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/gwtw/js-fibonacci-heap/issues" 28 | }, 29 | "homepage": "https://github.com/gwtw/js-fibonacci-heap", 30 | "devDependencies": { 31 | "@tyriar/heap-tests": "^1.0.2", 32 | "ava": "^0.14.0", 33 | "coveralls": "^2.11.4", 34 | "documentation-readme": "^2.1.0", 35 | "nyc": "^3.2.2", 36 | "xo": "^0.15.0" 37 | }, 38 | "xo": { 39 | "space": 2 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/clear-test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import Heap from '../'; 3 | import testHelper from '@tyriar/heap-tests/clear-tests'; 4 | 5 | testHelper.run(test, Heap); 6 | -------------------------------------------------------------------------------- /test/custom-compare-test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import Heap from '../'; 3 | import testHelper from '@tyriar/heap-tests/custom-compare-tests'; 4 | 5 | testHelper.run(test, Heap); 6 | -------------------------------------------------------------------------------- /test/decrease-key-test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import Heap from '../'; 3 | import testHelper from '@tyriar/heap-tests/decrease-key-tests'; 4 | 5 | testHelper.run(test, Heap); 6 | -------------------------------------------------------------------------------- /test/delete-test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import Heap from '../'; 3 | import testHelper from '@tyriar/heap-tests/delete-tests'; 4 | 5 | testHelper.run(test, Heap); 6 | -------------------------------------------------------------------------------- /test/extract-min-test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import Heap from '../'; 3 | import testHelper from '@tyriar/heap-tests/extract-min-tests'; 4 | 5 | testHelper.run(test, Heap); 6 | -------------------------------------------------------------------------------- /test/fibonacci-heap-decrease-key-test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import Heap from '../'; 3 | 4 | test('should leave a valid tree on a flat Fibonacci heap', function (t) { 5 | var heap = new Heap(); 6 | heap.insert(13, null); 7 | heap.insert(26, null); 8 | heap.insert(3, null); 9 | heap.insert(-6, null); 10 | heap.insert(27, null); 11 | var node6 = heap.insert(88, null); 12 | heap.insert(59, null); 13 | heap.insert(-10, null); 14 | heap.insert(16, null); 15 | heap.decreaseKey(node6, -8); 16 | t.deepEqual(heap.extractMinimum().key, -10); 17 | t.deepEqual(heap.extractMinimum().key, -8); 18 | t.deepEqual(heap.extractMinimum().key, -6); 19 | t.deepEqual(heap.extractMinimum().key, 3); 20 | t.deepEqual(heap.extractMinimum().key, 13); 21 | t.deepEqual(heap.extractMinimum().key, 16); 22 | t.deepEqual(heap.extractMinimum().key, 26); 23 | t.deepEqual(heap.extractMinimum().key, 27); 24 | t.deepEqual(heap.extractMinimum().key, 59); 25 | }); 26 | 27 | test('should leave a valid tree on a consolidated Fibonacci heap', function (t) { 28 | var heap = new Heap(); 29 | var node0 = heap.insert(0, null); 30 | var node1 = heap.insert(1, null); 31 | var node2 = heap.insert(2, null); 32 | var node3 = heap.insert(3, null); 33 | var node4 = heap.insert(4, null); 34 | var node5 = heap.insert(5, null); 35 | var node6 = heap.insert(6, null); 36 | var node7 = heap.insert(7, null); 37 | var node8 = heap.insert(8, null); 38 | 39 | // Extracting minimum should trigger consolidate. 40 | // 41 | // __1 42 | // / /| 43 | // 5 3 2 44 | // 0--1--2--3--4--5--6--7--8 -> /| | 45 | // 7 6 4 46 | // | 47 | // 8 48 | // 49 | t.is(heap.extractMinimum(), node0); 50 | 51 | // Decrease node 8 to 0 52 | // 53 | // __1 54 | // / /| __1--0 55 | // 5 3 2 / /| 56 | // /| | -> 5 3 2 57 | // 7 6 4 /| | 58 | // | 7 6 4 59 | // 8 60 | // 61 | heap.decreaseKey(node8, 0); 62 | t.true(node1.next === node8); 63 | 64 | t.is(heap.size(), 8); 65 | t.true(heap.extractMinimum() === node8); 66 | t.true(heap.extractMinimum() === node1); 67 | t.true(heap.extractMinimum() === node2); 68 | t.true(heap.extractMinimum() === node3); 69 | t.true(heap.extractMinimum() === node4); 70 | t.true(heap.extractMinimum() === node5); 71 | t.true(heap.extractMinimum() === node6); 72 | t.true(heap.extractMinimum() === node7); 73 | t.true(heap.isEmpty()); 74 | }); 75 | 76 | test('should delete the node\'s parent reference after a cut', function (t) { 77 | var heap = new Heap(); 78 | var node1 = heap.insert(1, null); 79 | heap.insert(2, null); 80 | var node3 = heap.insert(3, null); 81 | t.is(heap.size(), 3); 82 | 83 | // Trigger a consolidate 84 | // 85 | // 2 86 | // 1--2--3 -> | 87 | // 3 88 | // 89 | t.is(heap.extractMinimum(), node1); 90 | 91 | // Decrease 3's key such that it's less than its parent 92 | // 93 | // 2 1 94 | // | -> | 95 | // 3 2 96 | // 97 | heap.decreaseKey(node3, 1); 98 | 99 | // Ensure 1's parent is undefined (the link to 2 has been cut) 100 | t.is(node3.parent, undefined); 101 | }); 102 | -------------------------------------------------------------------------------- /test/fibonacci-heap-delete-test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import Heap from '../'; 3 | 4 | test('should delete nodes in a flat Fibonacci heap', function (t) { 5 | var heap = new Heap(); 6 | var node3 = heap.insert(13, null); 7 | var node4 = heap.insert(26, null); 8 | var node2 = heap.insert(3, null); 9 | var node1 = heap.insert(-6, null); 10 | var node5 = heap.insert(27, null); 11 | t.is(heap.size(), 5); 12 | heap.delete(node3); 13 | t.is(heap.size(), 4); 14 | t.is(heap.extractMinimum(), node1); 15 | t.is(heap.extractMinimum(), node2); 16 | t.is(heap.extractMinimum(), node4); 17 | t.is(heap.extractMinimum(), node5); 18 | t.true(heap.isEmpty()); 19 | }); 20 | 21 | test('should cut the node from the tree if the node is not the minimum it does not have a grandparent', function (t) { 22 | var heap = new Heap(); 23 | var node1 = heap.insert(1, null); 24 | var node2 = heap.insert(2, null); 25 | var node3 = heap.insert(3, null); 26 | var node4 = heap.insert(4, null); 27 | // Extract the minimum, forcing the construction of an order 2 tree which 28 | // is changed to an order 0 and order 1 tree after the minimum is extracted. 29 | // 30 | // 1 31 | // /| 3--2 32 | // 1--2--3--4 -> 3 2 -> | 33 | // | 4 34 | // 4 35 | // 36 | t.is(heap.extractMinimum(), node1); 37 | // Deleting the node should trigger a cut and cascadingCut on the heap. 38 | heap.delete(node4); 39 | 40 | t.is(heap.size(), 2); 41 | t.is(heap.extractMinimum(), node2); 42 | t.is(heap.extractMinimum(), node3); 43 | t.true(heap.isEmpty()); 44 | }); 45 | 46 | test('should cut the node from the tree if the node is not the minimum and it has a grandparent', function (t) { 47 | var heap = new Heap(); 48 | var node0 = heap.insert(0, null); 49 | var node1 = heap.insert(1, null); 50 | var node2 = heap.insert(2, null); 51 | var node3 = heap.insert(3, null); 52 | var node4 = heap.insert(4, null); 53 | var node5 = heap.insert(5, null); 54 | var node6 = heap.insert(6, null); 55 | var node7 = heap.insert(7, null); 56 | var node8 = heap.insert(8, null); 57 | 58 | // extractMinimum on 0 should trigger a cut and cascadingCut on the heap. 59 | // 60 | // __1 61 | // / /| 62 | // 5 3 2 63 | // 0--1--2--3--4--5--6--7--8 -> /| | 64 | // 7 6 4 65 | // | 66 | // 8 67 | // 68 | t.is(heap.extractMinimum(), node0); 69 | 70 | // Delete node 8 71 | // 72 | // __1 73 | // / /| __1 74 | // 5 3 2 / /| 75 | // /| | -> 5 3 2 76 | // 7 6 4 /| | 77 | // | 7 6 4 78 | // 8 79 | // 80 | heap.delete(node8); 81 | 82 | t.is(heap.size(), 7); 83 | t.is(heap.extractMinimum(), node1); 84 | t.is(heap.extractMinimum(), node2); 85 | t.is(heap.extractMinimum(), node3); 86 | t.is(heap.extractMinimum(), node4); 87 | t.is(heap.extractMinimum(), node5); 88 | t.is(heap.extractMinimum(), node6); 89 | t.is(heap.extractMinimum(), node7); 90 | t.true(heap.isEmpty()); 91 | }); 92 | 93 | test('should cut the node from the tree if the node is not the minimum, it has a grandparent and its parent is marked', function (t) { 94 | var heap = new Heap(); 95 | var node0 = heap.insert(0, null); 96 | var node1 = heap.insert(1, null); 97 | var node2 = heap.insert(2, null); 98 | var node3 = heap.insert(3, null); 99 | var node4 = heap.insert(4, null); 100 | var node5 = heap.insert(5, null); 101 | var node6 = heap.insert(6, null); 102 | var node7 = heap.insert(7, null); 103 | var node8 = heap.insert(8, null); 104 | 105 | // Extracting minimum should trigger consolidate. 106 | // 107 | // __1 108 | // / /| 109 | // 5 3 2 110 | // 0--1--2--3--4--5--6--7--8 -> /| | 111 | // 7 6 4 112 | // | 113 | // 8 114 | // 115 | t.is(heap.extractMinimum(), node0); 116 | 117 | // Delete node 6, marking 5 118 | // 119 | // __1 __1 120 | // / /| / /| 121 | // 5 3 2 >5 3 2 122 | // /| | -> / | 123 | // 7 6 4 7 4 124 | // | | 125 | // 8 8 126 | // 127 | heap.delete(node6); 128 | t.true(node5.isMarked); 129 | 130 | // Delete node 7, cutting the sub-tree 131 | // 132 | // __1 133 | // / /| 1--5 134 | // >5 3 2 /| | 135 | // / | -> 3 2 8 136 | // 7 4 | 137 | // | 4 138 | // 8 139 | // 140 | heap.delete(node7); 141 | t.true(node5.next === node1); 142 | t.true(node2.next === node3); 143 | t.true(node3.next === node2); 144 | 145 | t.is(heap.size(), 6); 146 | t.true(heap.extractMinimum() === node1); 147 | t.true(heap.extractMinimum() === node2); 148 | t.true(heap.extractMinimum() === node3); 149 | t.true(heap.extractMinimum() === node4); 150 | t.true(heap.extractMinimum() === node5); 151 | t.true(heap.extractMinimum() === node8); 152 | t.true(heap.isEmpty()); 153 | }); 154 | 155 | test('should correctly assign an indirect child when a direct child is cut from the parent', function (t) { 156 | var heap = new Heap(); 157 | var node0 = heap.insert(0, null); 158 | heap.insert(1, null); 159 | heap.insert(2, null); 160 | heap.insert(3, null); 161 | heap.insert(4, null); 162 | var node5 = heap.insert(5, null); 163 | var node6 = heap.insert(6, null); 164 | var node7 = heap.insert(7, null); 165 | heap.insert(8, null); 166 | 167 | // Extracting minimum should trigger consolidate. 168 | // 169 | // __1 170 | // / /| 171 | // 5 3 2 172 | // 0--1--2--3--4--5--6--7--8 -> /| | 173 | // 7 6 4 174 | // | 175 | // 8 176 | // 177 | t.is(heap.extractMinimum(), node0); 178 | 179 | // Delete node 6, marking 5 180 | // 181 | // __1 __1 182 | // / /| / /| 183 | // 5 3 2 >5 3 2 184 | // /| | -> / | 185 | // 7 6 4 7 4 186 | // | | 187 | // 8 8 188 | // 189 | heap.delete(node6); 190 | t.true(node5.child === node7); 191 | }); 192 | -------------------------------------------------------------------------------- /test/fibonacci-heap-extract-min-test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import Heap from '../'; 3 | 4 | test('should consolidate 8 nodes into a well formed order 1 tree', function (t) { 5 | var heap = new Heap(); 6 | var node0 = heap.insert(0, null); 7 | var node1 = heap.insert(1, null); 8 | var node2 = heap.insert(2, null); 9 | 10 | // Extracting minimum should trigger consolidate. 11 | // 12 | // 1 13 | // 0--1--2 -> | 14 | // 2 15 | // 16 | t.is(heap.extractMinimum(), node0); 17 | t.is(heap.size(), 2); 18 | t.true(node1.parent === undefined); 19 | t.true(node2.parent === node1); 20 | t.true(node1.next === node1); 21 | t.true(node2.next === node2); 22 | t.true(node1.child === node2); 23 | t.true(node2.child === undefined); 24 | }); 25 | 26 | test('should consolidate 8 nodes into a well formed order 2 tree', function (t) { 27 | var heap = new Heap(); 28 | var node0 = heap.insert(0, null); 29 | var node1 = heap.insert(1, null); 30 | var node2 = heap.insert(2, null); 31 | var node3 = heap.insert(3, null); 32 | var node4 = heap.insert(4, null); 33 | 34 | // Extracting minimum should trigger consolidate. 35 | // 36 | // 1 37 | // /| 38 | // 0--1--2--3--4 -> 3 2 39 | // | 40 | // 4 41 | // 42 | t.is(heap.extractMinimum(), node0); 43 | t.is(heap.size(), 4); 44 | t.true(node1.parent === undefined); 45 | t.true(node2.parent === node1); 46 | t.true(node3.parent === node1); 47 | t.true(node4.parent === node3); 48 | t.true(node1.next === node1); 49 | t.true(node2.next === node3); 50 | t.true(node3.next === node2); 51 | t.true(node4.next === node4); 52 | t.true(node1.child === node2); 53 | t.true(node2.child === undefined); 54 | t.true(node3.child === node4); 55 | t.true(node4.child === undefined); 56 | }); 57 | 58 | test('should consolidate 8 nodes into a well formed order 3 tree', function (t) { 59 | var heap = new Heap(); 60 | var node0 = heap.insert(0, null); 61 | var node1 = heap.insert(1, null); 62 | var node2 = heap.insert(2, null); 63 | var node3 = heap.insert(3, null); 64 | var node4 = heap.insert(4, null); 65 | var node5 = heap.insert(5, null); 66 | var node6 = heap.insert(6, null); 67 | var node7 = heap.insert(7, null); 68 | var node8 = heap.insert(8, null); 69 | 70 | // Extracting minimum should trigger consolidate. 71 | // 72 | // __1 73 | // / /| 74 | // 5 3 2 75 | // 1--2--3--4--5--6--7--8 -> /| | 76 | // 7 6 4 77 | // | 78 | // 8 79 | // 80 | t.is(heap.extractMinimum(), node0); 81 | t.true(node1.parent === undefined); 82 | t.true(node2.parent === node1); 83 | t.true(node3.parent === node1); 84 | t.true(node4.parent === node3); 85 | t.true(node5.parent === node1); 86 | t.true(node6.parent === node5); 87 | t.true(node7.parent === node5); 88 | t.true(node8.parent === node7); 89 | t.true(node1.next === node1); 90 | t.true(node2.next === node5); 91 | t.true(node3.next === node2); 92 | t.true(node4.next === node4); 93 | t.true(node5.next === node3); 94 | t.true(node6.next === node7); 95 | t.true(node7.next === node6); 96 | t.true(node8.next === node8); 97 | t.true(node1.child === node2); 98 | t.true(node2.child === undefined); 99 | t.true(node3.child === node4); 100 | t.true(node4.child === undefined); 101 | t.true(node5.child === node6); 102 | t.true(node6.child === undefined); 103 | t.true(node7.child === node8); 104 | t.true(node8.child === undefined); 105 | }); 106 | 107 | test('should consolidate after extract min is called on a tree with a single tree in the root node list', function (t) { 108 | var heap = new Heap(); 109 | var node0 = heap.insert(0, null); 110 | var node1 = heap.insert(1, null); 111 | var node2 = heap.insert(2, null); 112 | var node3 = heap.insert(3, null); 113 | heap.insert(4, null); 114 | heap.insert(5, null); 115 | heap.insert(6, null); 116 | heap.insert(7, null); 117 | heap.insert(8, null); 118 | 119 | // Extract minimum to trigger a consolidate nodes into a single Fibonacci tree. 120 | // 121 | // __1 122 | // / /| 123 | // 2 c d 124 | // 1--2--3--4--5--6--7--8 -> /| | 125 | // a b f 126 | // | 127 | // e 128 | // 129 | t.is(heap.extractMinimum(), node0); 130 | 131 | // Delete the 2nd smallest node in the heap which is the child of the single node in the root 132 | // list. After this operation is performed the minimum node (1) is no longer pointing the minimum 133 | // child in the node list! 134 | // 135 | // __1 136 | // / /| __1 137 | // 2 c d / /| Note that a in this illustration could also be b, the main thing 138 | // /| | -> a c d to take note of here is that a (or b) may not be the minimum child 139 | // a b f /| | of 1 anymore, despite being the node it's linked to. 140 | // | e b f 141 | // e 142 | // 143 | heap.delete(node2); 144 | 145 | // Extracting the minimum node at this point must trigger a consolidate on the new list, otherwise 146 | // the next minimum may not be the actual minimum. 147 | // 148 | // 149 | // __1 150 | // / /| a---c---d 151 | // a c d -> /| | -> Consolidate now to ensure the heap's minimum is correct 152 | // /| | e b f 153 | // e b f 154 | // 155 | // 156 | t.is(heap.extractMinimum(), node1); 157 | t.is(heap.findMinimum(), node3); 158 | }); 159 | -------------------------------------------------------------------------------- /test/find-minimum-test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import Heap from '../'; 3 | import testHelper from '@tyriar/heap-tests/find-minimum-tests'; 4 | 5 | testHelper.run(test, Heap); 6 | -------------------------------------------------------------------------------- /test/insert-test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import Heap from '../'; 3 | import testHelper from '@tyriar/heap-tests/insert-tests'; 4 | 5 | testHelper.run(test, Heap); 6 | -------------------------------------------------------------------------------- /test/is-empty-test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import Heap from '../'; 3 | import testHelper from '@tyriar/heap-tests/is-empty-tests'; 4 | 5 | testHelper.run(test, Heap); 6 | -------------------------------------------------------------------------------- /test/size-test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import Heap from '../'; 3 | import testHelper from '@tyriar/heap-tests/size-tests'; 4 | 5 | testHelper.run(test, Heap); 6 | -------------------------------------------------------------------------------- /test/union-test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import Heap from '../'; 3 | import testHelper from '@tyriar/heap-tests/union-tests'; 4 | 5 | testHelper.run(test, Heap); 6 | --------------------------------------------------------------------------------