BinaryHeap.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. /*
  2. ** Binary Heap implementation in Javascript
  3. ** From: http://eloquentjavascript.net/1st_edition/appendix2.html
  4. **
  5. ** Copyright (c) 2007 Marijn Haverbeke, last modified on November 28 2013.
  6. **
  7. ** Licensed under a Creative Commons attribution-noncommercial license.
  8. ** All code in this book may also be considered licensed under an MIT license.
  9. */
  10. function BinaryHeap(scoreFunction){
  11. this.content = [];
  12. this.scoreFunction = scoreFunction;
  13. }
  14. BinaryHeap.prototype = {
  15. push: function(element) {
  16. // Add the new element to the end of the array.
  17. this.content.push(element);
  18. // Allow it to bubble up.
  19. this.bubbleUp(this.content.length - 1);
  20. },
  21. pop: function() {
  22. // Store the first element so we can return it later.
  23. var result = this.content[0];
  24. // Get the element at the end of the array.
  25. var end = this.content.pop();
  26. // If there are any elements left, put the end element at the
  27. // start, and let it sink down.
  28. if (this.content.length > 0) {
  29. this.content[0] = end;
  30. this.sinkDown(0);
  31. }
  32. return result;
  33. },
  34. remove: function(node) {
  35. var length = this.content.length;
  36. // To remove a value, we must search through the array to find
  37. // it.
  38. for (var i = 0; i < length; i++) {
  39. if (this.content[i] != node) continue;
  40. // When it is found, the process seen in 'pop' is repeated
  41. // to fill up the hole.
  42. var end = this.content.pop();
  43. // If the element we popped was the one we needed to remove,
  44. // we're done.
  45. if (i == length - 1) break;
  46. // Otherwise, we replace the removed element with the popped
  47. // one, and allow it to float up or sink down as appropriate.
  48. this.content[i] = end;
  49. this.bubbleUp(i);
  50. this.sinkDown(i);
  51. break;
  52. }
  53. },
  54. size: function() {
  55. return this.content.length;
  56. },
  57. bubbleUp: function(n) {
  58. // Fetch the element that has to be moved.
  59. var element = this.content[n], score = this.scoreFunction(element);
  60. // When at 0, an element can not go up any further.
  61. while (n > 0) {
  62. // Compute the parent element's index, and fetch it.
  63. var parentN = Math.floor((n + 1) / 2) - 1,
  64. parent = this.content[parentN];
  65. // If the parent has a lesser score, things are in order and we
  66. // are done.
  67. if (score >= this.scoreFunction(parent))
  68. break;
  69. // Otherwise, swap the parent with the current element and
  70. // continue.
  71. this.content[parentN] = element;
  72. this.content[n] = parent;
  73. n = parentN;
  74. }
  75. },
  76. sinkDown: function(n) {
  77. // Look up the target element and its score.
  78. var length = this.content.length,
  79. element = this.content[n],
  80. elemScore = this.scoreFunction(element);
  81. while(true) {
  82. // Compute the indices of the child elements.
  83. var child2N = (n + 1) * 2, child1N = child2N - 1;
  84. // This is used to store the new position of the element,
  85. // if any.
  86. var swap = null;
  87. // If the first child exists (is inside the array)...
  88. if (child1N < length) {
  89. // Look it up and compute its score.
  90. var child1 = this.content[child1N],
  91. child1Score = this.scoreFunction(child1);
  92. // If the score is less than our element's, we need to swap.
  93. if (child1Score < elemScore)
  94. swap = child1N;
  95. }
  96. // Do the same checks for the other child.
  97. if (child2N < length) {
  98. var child2 = this.content[child2N],
  99. child2Score = this.scoreFunction(child2);
  100. if (child2Score < (swap == null ? elemScore : child1Score))
  101. swap = child2N;
  102. }
  103. // No need to swap further, we are done.
  104. if (swap == null) break;
  105. // Otherwise, swap and continue.
  106. this.content[n] = this.content[swap];
  107. this.content[swap] = element;
  108. n = swap;
  109. }
  110. }
  111. };