heap.coffee 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. {floor, min} = Math
  2. ###
  3. Default comparison function to be used
  4. ###
  5. defaultCmp = (x, y) ->
  6. return -1 if x < y
  7. return 1 if x > y
  8. 0
  9. ###
  10. Insert item x in list a, and keep it sorted assuming a is sorted.
  11. If x is already in a, insert it to the right of the rightmost x.
  12. Optional args lo (default 0) and hi (default a.length) bound the slice
  13. of a to be searched.
  14. ###
  15. insort = (a, x, lo=0, hi, cmp=defaultCmp) ->
  16. throw new Error('lo must be non-negative') if lo < 0
  17. hi ?= a.length
  18. while lo < hi
  19. mid = floor((lo + hi) / 2)
  20. if cmp(x, a[mid]) < 0
  21. hi = mid
  22. else
  23. lo = mid + 1
  24. a[lo...lo] = x
  25. ###
  26. Push item onto heap, maintaining the heap invariant.
  27. ###
  28. heappush = (array, item, cmp=defaultCmp) ->
  29. array.push(item)
  30. _siftdown(array, 0, array.length - 1, cmp)
  31. ###
  32. Pop the smallest item off the heap, maintaining the heap invariant.
  33. ###
  34. heappop = (array, cmp=defaultCmp) ->
  35. lastelt = array.pop()
  36. if array.length
  37. returnitem = array[0]
  38. array[0] = lastelt
  39. _siftup(array, 0, cmp)
  40. else
  41. returnitem = lastelt
  42. returnitem
  43. ###
  44. Pop and return the current smallest value, and add the new item.
  45. This is more efficient than heappop() followed by heappush(), and can be
  46. more appropriate when using a fixed size heap. Note that the value
  47. returned may be larger than item! That constrains reasonable use of
  48. this routine unless written as part of a conditional replacement:
  49. if item > array[0]
  50. item = heapreplace(array, item)
  51. ###
  52. heapreplace = (array, item, cmp=defaultCmp) ->
  53. returnitem = array[0]
  54. array[0] = item
  55. _siftup(array, 0, cmp)
  56. returnitem
  57. ###
  58. Fast version of a heappush followed by a heappop.
  59. ###
  60. heappushpop = (array, item, cmp=defaultCmp) ->
  61. if array.length and cmp(array[0], item) < 0
  62. [item, array[0]] = [array[0], item]
  63. _siftup(array, 0, cmp)
  64. item
  65. ###
  66. Transform list into a heap, in-place, in O(array.length) time.
  67. ###
  68. heapify = (array, cmp=defaultCmp) ->
  69. for i in [0...floor(array.length / 2)].reverse()
  70. _siftup(array, i, cmp)
  71. ###
  72. Update the position of the given item in the heap.
  73. This function should be called every time the item is being modified.
  74. ###
  75. updateItem = (array, item, cmp=defaultCmp) ->
  76. pos = array.indexOf(item)
  77. return if pos is -1
  78. _siftdown(array, 0, pos, cmp)
  79. _siftup(array, pos, cmp)
  80. ###
  81. Find the n largest elements in a dataset.
  82. ###
  83. nlargest = (array, n, cmp=defaultCmp) ->
  84. result = array[0...n]
  85. return result unless result.length
  86. heapify(result, cmp)
  87. heappushpop(result, elem, cmp) for elem in array[n..]
  88. result.sort(cmp).reverse()
  89. ###
  90. Find the n smallest elements in a dataset.
  91. ###
  92. nsmallest = (array, n, cmp=defaultCmp) ->
  93. if n * 10 <= array.length
  94. result = array[0...n].sort(cmp)
  95. return result unless result.length
  96. los = result[result.length - 1]
  97. for elem in array[n..]
  98. if cmp(elem, los) < 0
  99. insort(result, elem, 0, null, cmp)
  100. result.pop()
  101. los = result[result.length - 1]
  102. return result
  103. heapify(array, cmp)
  104. (heappop(array, cmp) for i in [0...min(n, array.length)])
  105. _siftdown = (array, startpos, pos, cmp=defaultCmp) ->
  106. newitem = array[pos]
  107. while pos > startpos
  108. parentpos = (pos - 1) >> 1
  109. parent = array[parentpos]
  110. if cmp(newitem, parent) < 0
  111. array[pos] = parent
  112. pos = parentpos
  113. continue
  114. break
  115. array[pos] = newitem
  116. _siftup = (array, pos, cmp=defaultCmp) ->
  117. endpos = array.length
  118. startpos = pos
  119. newitem = array[pos]
  120. childpos = 2 * pos + 1
  121. while childpos < endpos
  122. rightpos = childpos + 1
  123. if rightpos < endpos and not (cmp(array[childpos], array[rightpos]) < 0)
  124. childpos = rightpos
  125. array[pos] = array[childpos]
  126. pos = childpos
  127. childpos = 2 * pos + 1
  128. array[pos] = newitem
  129. _siftdown(array, startpos, pos, cmp)
  130. class Heap
  131. @push: heappush
  132. @pop: heappop
  133. @replace: heapreplace
  134. @pushpop: heappushpop
  135. @heapify: heapify
  136. @updateItem: updateItem
  137. @nlargest: nlargest
  138. @nsmallest: nsmallest
  139. constructor: (@cmp=defaultCmp) ->
  140. @nodes = []
  141. push: (x) ->
  142. heappush(@nodes, x, @cmp)
  143. pop: ->
  144. heappop(@nodes, @cmp)
  145. peek: ->
  146. @nodes[0]
  147. contains: (x) ->
  148. @nodes.indexOf(x) isnt -1
  149. replace: (x) ->
  150. heapreplace(@nodes, x, @cmp)
  151. pushpop: (x) ->
  152. heappushpop(@nodes, x, @cmp)
  153. heapify: ->
  154. heapify(@nodes, @cmp)
  155. updateItem: (x) ->
  156. updateItem(@nodes, x, @cmp)
  157. clear: ->
  158. @nodes = []
  159. empty: ->
  160. @nodes.length is 0
  161. size: ->
  162. @nodes.length
  163. clone: ->
  164. heap = new Heap()
  165. heap.nodes = @nodes.slice(0)
  166. heap
  167. toArray: ->
  168. @nodes.slice(0)
  169. # aliases
  170. insert: @::push
  171. top: @::peek
  172. front: @::peek
  173. has: @::contains
  174. copy: @::clone
  175. # exports to global
  176. ((root, factory) ->
  177. if typeof define is 'function' and define.amd
  178. define [], factory
  179. else if typeof exports is 'object'
  180. module.exports = factory()
  181. else
  182. root.Heap = factory()
  183. ) @, -> Heap