priority-queue.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. const _ = require('../lodash')
  2. module.exports = PriorityQueue
  3. /**
  4. * A min-priority queue data structure. This algorithm is derived from Cormen,
  5. * et al., "Introduction to Algorithms". The basic idea of a min-priority
  6. * queue is that you can efficiently (in O(1) time) get the smallest key in
  7. * the queue. Adding and removing elements takes O(log n) time. A key can
  8. * have its priority decreased in O(log n) time.
  9. */
  10. function PriorityQueue () {
  11. this._arr = []
  12. this._keyIndices = {}
  13. }
  14. /**
  15. * Returns the number of elements in the queue. Takes `O(1)` time.
  16. */
  17. PriorityQueue.prototype.size = function () {
  18. return this._arr.length
  19. }
  20. /**
  21. * Returns the keys that are in the queue. Takes `O(n)` time.
  22. */
  23. PriorityQueue.prototype.keys = function () {
  24. return this._arr.map(function (x) { return x.key })
  25. }
  26. /**
  27. * Returns `true` if **key** is in the queue and `false` if not.
  28. */
  29. PriorityQueue.prototype.has = function (key) {
  30. return _.has(this._keyIndices, key)
  31. }
  32. /**
  33. * Returns the priority for **key**. If **key** is not present in the queue
  34. * then this function returns `undefined`. Takes `O(1)` time.
  35. *
  36. * @param {Object} key
  37. */
  38. PriorityQueue.prototype.priority = function (key) {
  39. var index = this._keyIndices[key]
  40. if (index !== undefined) {
  41. return this._arr[index].priority
  42. }
  43. }
  44. /**
  45. * Returns the key for the minimum element in this queue. If the queue is
  46. * empty this function throws an Error. Takes `O(1)` time.
  47. */
  48. PriorityQueue.prototype.min = function () {
  49. if (this.size() === 0) {
  50. throw new Error('Queue underflow')
  51. }
  52. return this._arr[0].key
  53. }
  54. /**
  55. * Inserts a new key into the priority queue. If the key already exists in
  56. * the queue this function returns `false`; otherwise it will return `true`.
  57. * Takes `O(n)` time.
  58. *
  59. * @param {Object} key the key to add
  60. * @param {Number} priority the initial priority for the key
  61. */
  62. PriorityQueue.prototype.add = function (key, priority) {
  63. var keyIndices = this._keyIndices
  64. key = String(key)
  65. if (!_.has(keyIndices, key)) {
  66. var arr = this._arr
  67. var index = arr.length
  68. keyIndices[key] = index
  69. arr.push({key: key, priority: priority})
  70. this._decrease(index)
  71. return true
  72. }
  73. return false
  74. }
  75. /**
  76. * Removes and returns the smallest key in the queue. Takes `O(log n)` time.
  77. */
  78. PriorityQueue.prototype.removeMin = function () {
  79. this._swap(0, this._arr.length - 1)
  80. var min = this._arr.pop()
  81. delete this._keyIndices[min.key]
  82. this._heapify(0)
  83. return min.key
  84. }
  85. /**
  86. * Decreases the priority for **key** to **priority**. If the new priority is
  87. * greater than the previous priority, this function will throw an Error.
  88. *
  89. * @param {Object} key the key for which to raise priority
  90. * @param {Number} priority the new priority for the key
  91. */
  92. PriorityQueue.prototype.decrease = function (key, priority) {
  93. var index = this._keyIndices[key]
  94. if (priority > this._arr[index].priority) {
  95. throw new Error('New priority is greater than current priority. ' +
  96. 'Key: ' + key + ' Old: ' + this._arr[index].priority + ' New: ' + priority)
  97. }
  98. this._arr[index].priority = priority
  99. this._decrease(index)
  100. }
  101. PriorityQueue.prototype._heapify = function (i) {
  102. const arr = this._arr
  103. const l = 2 * i
  104. const r = l + 1
  105. let largest = i
  106. if (l < arr.length) {
  107. largest = arr[l].priority < arr[largest].priority ? l : largest
  108. if (r < arr.length) {
  109. largest = arr[r].priority < arr[largest].priority ? r : largest
  110. }
  111. if (largest !== i) {
  112. this._swap(i, largest)
  113. this._heapify(largest)
  114. }
  115. }
  116. }
  117. PriorityQueue.prototype._decrease = function (index) {
  118. var arr = this._arr
  119. var priority = arr[index].priority
  120. var parent
  121. while (index !== 0) {
  122. parent = index >> 1
  123. if (arr[parent].priority < priority) {
  124. break
  125. }
  126. this._swap(index, parent)
  127. index = parent
  128. }
  129. }
  130. PriorityQueue.prototype._swap = function (i, j) {
  131. var arr = this._arr
  132. var keyIndices = this._keyIndices
  133. var origArrI = arr[i]
  134. var origArrJ = arr[j]
  135. arr[i] = origArrJ
  136. arr[j] = origArrI
  137. keyIndices[origArrJ.key] = i
  138. keyIndices[origArrI.key] = j
  139. }