Heap.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import Check from "./Check.js";
  2. import defaultValue from "./defaultValue.js";
  3. import defined from "./defined.js";
  4. /**
  5. * Array implementation of a heap.
  6. *
  7. * @alias Heap
  8. * @constructor
  9. * @private
  10. *
  11. * @param {Object} options Object with the following properties:
  12. * @param {Heap.ComparatorCallback} options.comparator The comparator to use for the heap. If comparator(a, b) is less than 0, sort a to a lower index than b, otherwise sort to a higher index.
  13. */
  14. function Heap(options) {
  15. //>>includeStart('debug', pragmas.debug);
  16. Check.typeOf.object("options", options);
  17. Check.defined("options.comparator", options.comparator);
  18. //>>includeEnd('debug');
  19. this._comparator = options.comparator;
  20. this._array = [];
  21. this._length = 0;
  22. this._maximumLength = undefined;
  23. }
  24. Object.defineProperties(Heap.prototype, {
  25. /**
  26. * Gets the length of the heap.
  27. *
  28. * @memberof Heap.prototype
  29. *
  30. * @type {Number}
  31. * @readonly
  32. */
  33. length: {
  34. get: function () {
  35. return this._length;
  36. },
  37. },
  38. /**
  39. * Gets the internal array.
  40. *
  41. * @memberof Heap.prototype
  42. *
  43. * @type {Array}
  44. * @readonly
  45. */
  46. internalArray: {
  47. get: function () {
  48. return this._array;
  49. },
  50. },
  51. /**
  52. * Gets and sets the maximum length of the heap.
  53. *
  54. * @memberof Heap.prototype
  55. *
  56. * @type {Number}
  57. */
  58. maximumLength: {
  59. get: function () {
  60. return this._maximumLength;
  61. },
  62. set: function (value) {
  63. //>>includeStart('debug', pragmas.debug);
  64. Check.typeOf.number.greaterThanOrEquals("maximumLength", value, 0);
  65. //>>includeEnd('debug');
  66. const originalLength = this._length;
  67. if (value < originalLength) {
  68. const array = this._array;
  69. // Remove trailing references
  70. for (let i = value; i < originalLength; ++i) {
  71. array[i] = undefined;
  72. }
  73. this._length = value;
  74. array.length = value;
  75. }
  76. this._maximumLength = value;
  77. },
  78. },
  79. /**
  80. * The comparator to use for the heap. If comparator(a, b) is less than 0, sort a to a lower index than b, otherwise sort to a higher index.
  81. *
  82. * @memberof Heap.prototype
  83. *
  84. * @type {Heap.ComparatorCallback}
  85. */
  86. comparator: {
  87. get: function () {
  88. return this._comparator;
  89. },
  90. },
  91. });
  92. function swap(array, a, b) {
  93. const temp = array[a];
  94. array[a] = array[b];
  95. array[b] = temp;
  96. }
  97. /**
  98. * Resizes the internal array of the heap.
  99. *
  100. * @param {Number} [length] The length to resize internal array to. Defaults to the current length of the heap.
  101. */
  102. Heap.prototype.reserve = function (length) {
  103. length = defaultValue(length, this._length);
  104. this._array.length = length;
  105. };
  106. /**
  107. * Update the heap so that index and all descendants satisfy the heap property.
  108. *
  109. * @param {Number} [index=0] The starting index to heapify from.
  110. */
  111. Heap.prototype.heapify = function (index) {
  112. index = defaultValue(index, 0);
  113. const length = this._length;
  114. const comparator = this._comparator;
  115. const array = this._array;
  116. let candidate = -1;
  117. let inserting = true;
  118. while (inserting) {
  119. const right = 2 * (index + 1);
  120. const left = right - 1;
  121. if (left < length && comparator(array[left], array[index]) < 0) {
  122. candidate = left;
  123. } else {
  124. candidate = index;
  125. }
  126. if (right < length && comparator(array[right], array[candidate]) < 0) {
  127. candidate = right;
  128. }
  129. if (candidate !== index) {
  130. swap(array, candidate, index);
  131. index = candidate;
  132. } else {
  133. inserting = false;
  134. }
  135. }
  136. };
  137. /**
  138. * Resort the heap.
  139. */
  140. Heap.prototype.resort = function () {
  141. const length = this._length;
  142. for (let i = Math.ceil(length / 2); i >= 0; --i) {
  143. this.heapify(i);
  144. }
  145. };
  146. /**
  147. * Insert an element into the heap. If the length would grow greater than maximumLength
  148. * of the heap, extra elements are removed.
  149. *
  150. * @param {*} element The element to insert
  151. *
  152. * @return {*} The element that was removed from the heap if the heap is at full capacity.
  153. */
  154. Heap.prototype.insert = function (element) {
  155. //>>includeStart('debug', pragmas.debug);
  156. Check.defined("element", element);
  157. //>>includeEnd('debug');
  158. const array = this._array;
  159. const comparator = this._comparator;
  160. const maximumLength = this._maximumLength;
  161. let index = this._length++;
  162. if (index < array.length) {
  163. array[index] = element;
  164. } else {
  165. array.push(element);
  166. }
  167. while (index !== 0) {
  168. const parent = Math.floor((index - 1) / 2);
  169. if (comparator(array[index], array[parent]) < 0) {
  170. swap(array, index, parent);
  171. index = parent;
  172. } else {
  173. break;
  174. }
  175. }
  176. let removedElement;
  177. if (defined(maximumLength) && this._length > maximumLength) {
  178. removedElement = array[maximumLength];
  179. this._length = maximumLength;
  180. }
  181. return removedElement;
  182. };
  183. /**
  184. * Remove the element specified by index from the heap and return it.
  185. *
  186. * @param {Number} [index=0] The index to remove.
  187. * @returns {*} The specified element of the heap.
  188. */
  189. Heap.prototype.pop = function (index) {
  190. index = defaultValue(index, 0);
  191. if (this._length === 0) {
  192. return undefined;
  193. }
  194. //>>includeStart('debug', pragmas.debug);
  195. Check.typeOf.number.lessThan("index", index, this._length);
  196. //>>includeEnd('debug');
  197. const array = this._array;
  198. const root = array[index];
  199. swap(array, index, --this._length);
  200. this.heapify(index);
  201. array[this._length] = undefined; // Remove trailing reference
  202. return root;
  203. };
  204. /**
  205. * The comparator to use for the heap.
  206. * @callback Heap.ComparatorCallback
  207. * @param {*} a An element in the heap.
  208. * @param {*} b An element in the heap.
  209. * @returns {Number} If the result of the comparison is less than 0, sort a to a lower index than b, otherwise sort to a higher index.
  210. */
  211. export default Heap;