Heap.js 6.4 KB

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