heapqueue.nim 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2016 Yuriy Glukhov
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. ## The `heapqueue` module implements a
  9. ## `binary heap data structure<https://en.wikipedia.org/wiki/Binary_heap>`_
  10. ## that can be used as a `priority queue<https://en.wikipedia.org/wiki/Priority_queue>`_.
  11. ## They are represented as arrays for which `a[k] <= a[2*k+1]` and `a[k] <= a[2*k+2]`
  12. ## for all indices `k` (counting elements from 0). The interesting property of a heap is that
  13. ## `a[0]` is always its smallest element.
  14. ##
  15. ## Basic usage
  16. ## -----------
  17. ##
  18. runnableExamples:
  19. var heap = [8, 2].toHeapQueue
  20. heap.push(5)
  21. # the first element is the lowest element
  22. assert heap[0] == 2
  23. # remove and return the lowest element
  24. assert heap.pop() == 2
  25. # the lowest element remaining is 5
  26. assert heap[0] == 5
  27. ## Usage with custom objects
  28. ## -------------------------
  29. ## To use a `HeapQueue` with a custom object, the `<` operator must be
  30. ## implemented.
  31. runnableExamples:
  32. type Job = object
  33. priority: int
  34. proc `<`(a, b: Job): bool = a.priority < b.priority
  35. var jobs = initHeapQueue[Job]()
  36. jobs.push(Job(priority: 1))
  37. jobs.push(Job(priority: 2))
  38. assert jobs[0].priority == 1
  39. import std/private/since
  40. type HeapQueue*[T] = object
  41. ## A heap queue, commonly known as a priority queue.
  42. data: seq[T]
  43. proc initHeapQueue*[T](): HeapQueue[T] =
  44. ## Creates a new empty heap.
  45. ##
  46. ## Heaps are initialized by default, so it is not necessary to call
  47. ## this function explicitly.
  48. ##
  49. ## **See also:**
  50. ## * `toHeapQueue proc <#toHeapQueue,openArray[T]>`_
  51. discard
  52. proc len*[T](heap: HeapQueue[T]): int {.inline.} =
  53. ## Returns the number of elements of `heap`.
  54. runnableExamples:
  55. let heap = [9, 5, 8].toHeapQueue
  56. assert heap.len == 3
  57. heap.data.len
  58. proc `[]`*[T](heap: HeapQueue[T], i: Natural): lent T {.inline.} =
  59. ## Accesses the i-th element of `heap`.
  60. heap.data[i]
  61. proc heapCmp[T](x, y: T): bool {.inline.} = x < y
  62. proc siftup[T](heap: var HeapQueue[T], startpos, p: int) =
  63. ## `heap` is a heap at all indices >= `startpos`, except possibly for `p`. `p`
  64. ## is the index of a leaf with a possibly out-of-order value. Restores the
  65. ## heap invariant.
  66. var pos = p
  67. let newitem = heap[pos]
  68. # Follow the path to the root, moving parents down until finding a place
  69. # newitem fits.
  70. while pos > startpos:
  71. let parentpos = (pos - 1) shr 1
  72. let parent = heap[parentpos]
  73. if heapCmp(newitem, parent):
  74. heap.data[pos] = parent
  75. pos = parentpos
  76. else:
  77. break
  78. heap.data[pos] = newitem
  79. proc siftdownToBottom[T](heap: var HeapQueue[T], p: int) =
  80. # This is faster when the element should be close to the bottom.
  81. let endpos = len(heap)
  82. var pos = p
  83. let startpos = pos
  84. let newitem = heap[pos]
  85. # Bubble up the smaller child until hitting a leaf.
  86. var childpos = 2 * pos + 1 # leftmost child position
  87. while childpos < endpos:
  88. # Set childpos to index of smaller child.
  89. let rightpos = childpos + 1
  90. if rightpos < endpos and not heapCmp(heap[childpos], heap[rightpos]):
  91. childpos = rightpos
  92. # Move the smaller child up.
  93. heap.data[pos] = heap[childpos]
  94. pos = childpos
  95. childpos = 2 * pos + 1
  96. # The leaf at pos is empty now. Put newitem there, and bubble it up
  97. # to its final resting place (by sifting its parents down).
  98. heap.data[pos] = newitem
  99. siftup(heap, startpos, pos)
  100. proc siftdown[T](heap: var HeapQueue[T], p: int) =
  101. let endpos = len(heap)
  102. var pos = p
  103. let newitem = heap[pos]
  104. var childpos = 2 * pos + 1
  105. while childpos < endpos:
  106. let rightpos = childpos + 1
  107. if rightpos < endpos and not heapCmp(heap[childpos], heap[rightpos]):
  108. childpos = rightpos
  109. if not heapCmp(heap[childpos], newitem):
  110. break
  111. heap.data[pos] = heap[childpos]
  112. pos = childpos
  113. childpos = 2 * pos + 1
  114. heap.data[pos] = newitem
  115. proc push*[T](heap: var HeapQueue[T], item: sink T) =
  116. ## Pushes `item` onto `heap`, maintaining the heap invariant.
  117. heap.data.add(item)
  118. siftup(heap, 0, len(heap) - 1)
  119. proc toHeapQueue*[T](x: openArray[T]): HeapQueue[T] {.since: (1, 3).} =
  120. ## Creates a new HeapQueue that contains the elements of `x`.
  121. ##
  122. ## **See also:**
  123. ## * `initHeapQueue proc <#initHeapQueue>`_
  124. runnableExamples:
  125. var heap = [9, 5, 8].toHeapQueue
  126. assert heap.pop() == 5
  127. assert heap[0] == 8
  128. # see https://en.wikipedia.org/wiki/Binary_heap#Building_a_heap
  129. result.data = @x
  130. for i in countdown(x.len div 2 - 1, 0):
  131. siftdown(result, i)
  132. proc pop*[T](heap: var HeapQueue[T]): T =
  133. ## Pops and returns the smallest item from `heap`,
  134. ## maintaining the heap invariant.
  135. runnableExamples:
  136. var heap = [9, 5, 8].toHeapQueue
  137. assert heap.pop() == 5
  138. let lastelt = heap.data.pop()
  139. if heap.len > 0:
  140. result = heap[0]
  141. heap.data[0] = lastelt
  142. siftdownToBottom(heap, 0)
  143. else:
  144. result = lastelt
  145. proc find*[T](heap: HeapQueue[T], x: T): int {.since: (1, 3).} =
  146. ## Linear scan to find the index of the item `x` or -1 if not found.
  147. runnableExamples:
  148. let heap = [9, 5, 8].toHeapQueue
  149. assert heap.find(5) == 0
  150. assert heap.find(9) == 1
  151. assert heap.find(777) == -1
  152. result = -1
  153. for i in 0 ..< heap.len:
  154. if heap[i] == x: return i
  155. proc del*[T](heap: var HeapQueue[T], index: Natural) =
  156. ## Removes the element at `index` from `heap`, maintaining the heap invariant.
  157. runnableExamples:
  158. var heap = [9, 5, 8].toHeapQueue
  159. heap.del(1)
  160. assert heap[0] == 5
  161. assert heap[1] == 8
  162. swap(heap.data[^1], heap.data[index])
  163. let newLen = heap.len - 1
  164. heap.data.setLen(newLen)
  165. if index < newLen:
  166. siftdownToBottom(heap, index)
  167. proc replace*[T](heap: var HeapQueue[T], item: sink T): T =
  168. ## Pops and returns the current smallest value, and add the new item.
  169. ## This is more efficient than `pop()` followed by `push()`, and can be
  170. ## more appropriate when using a fixed-size heap. Note that the value
  171. ## returned may be larger than `item`! That constrains reasonable uses of
  172. ## this routine unless written as part of a conditional replacement.
  173. ##
  174. ## **See also:**
  175. ## * `pushpop proc <#pushpop,HeapQueue[T],sinkT>`_
  176. runnableExamples:
  177. var heap = [5, 12].toHeapQueue
  178. assert heap.replace(6) == 5
  179. assert heap.len == 2
  180. assert heap[0] == 6
  181. assert heap.replace(4) == 6
  182. result = heap[0]
  183. heap.data[0] = item
  184. siftdown(heap, 0)
  185. proc pushpop*[T](heap: var HeapQueue[T], item: sink T): T =
  186. ## Fast version of a `push()` followed by a `pop()`.
  187. ##
  188. ## **See also:**
  189. ## * `replace proc <#replace,HeapQueue[T],sinkT>`_
  190. runnableExamples:
  191. var heap = [5, 12].toHeapQueue
  192. assert heap.pushpop(6) == 5
  193. assert heap.len == 2
  194. assert heap[0] == 6
  195. assert heap.pushpop(4) == 4
  196. result = item
  197. if heap.len > 0 and heapCmp(heap.data[0], result):
  198. swap(result, heap.data[0])
  199. siftdown(heap, 0)
  200. proc clear*[T](heap: var HeapQueue[T]) =
  201. ## Removes all elements from `heap`, making it empty.
  202. runnableExamples:
  203. var heap = [9, 5, 8].toHeapQueue
  204. heap.clear()
  205. assert heap.len == 0
  206. heap.data.setLen(0)
  207. proc `$`*[T](heap: HeapQueue[T]): string =
  208. ## Turns a heap into its string representation.
  209. runnableExamples:
  210. let heap = [1, 2].toHeapQueue
  211. assert $heap == "[1, 2]"
  212. result = "["
  213. for x in heap.data:
  214. if result.len > 1: result.add(", ")
  215. result.addQuoted(x)
  216. result.add("]")