heapqueue.nim 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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. when defined(nimPreviewSlimSystem):
  41. import std/assertions
  42. type HeapQueue*[T] = object
  43. ## A heap queue, commonly known as a priority queue.
  44. data: seq[T]
  45. proc initHeapQueue*[T](): HeapQueue[T] =
  46. ## Creates a new empty heap.
  47. ##
  48. ## Heaps are initialized by default, so it is not necessary to call
  49. ## this function explicitly.
  50. ##
  51. ## **See also:**
  52. ## * `toHeapQueue proc <#toHeapQueue,openArray[T]>`_
  53. result = default(HeapQueue[T])
  54. proc len*[T](heap: HeapQueue[T]): int {.inline.} =
  55. ## Returns the number of elements of `heap`.
  56. runnableExamples:
  57. let heap = [9, 5, 8].toHeapQueue
  58. assert heap.len == 3
  59. heap.data.len
  60. proc `[]`*[T](heap: HeapQueue[T], i: Natural): lent T {.inline.} =
  61. ## Accesses the i-th element of `heap`.
  62. heap.data[i]
  63. iterator items*[T](heap: HeapQueue[T]): lent T {.inline, since: (2, 1, 1).} =
  64. ## Iterates over each item of `heap`.
  65. let L = len(heap)
  66. for i in 0 .. high(heap.data):
  67. yield heap.data[i]
  68. assert(len(heap) == L, "the length of the HeapQueue changed while iterating over it")
  69. proc heapCmp[T](x, y: T): bool {.inline.} = x < y
  70. proc siftup[T](heap: var HeapQueue[T], startpos, p: int) =
  71. ## `heap` is a heap at all indices >= `startpos`, except possibly for `p`. `p`
  72. ## is the index of a leaf with a possibly out-of-order value. Restores the
  73. ## heap invariant.
  74. var pos = p
  75. let newitem = heap[pos]
  76. # Follow the path to the root, moving parents down until finding a place
  77. # newitem fits.
  78. while pos > startpos:
  79. let parentpos = (pos - 1) shr 1
  80. let parent = heap[parentpos]
  81. if heapCmp(newitem, parent):
  82. heap.data[pos] = parent
  83. pos = parentpos
  84. else:
  85. break
  86. heap.data[pos] = newitem
  87. proc siftdownToBottom[T](heap: var HeapQueue[T], p: int) =
  88. # This is faster when the element should be close to the bottom.
  89. let endpos = len(heap)
  90. var pos = p
  91. let startpos = pos
  92. let newitem = heap[pos]
  93. # Bubble up the smaller child until hitting a leaf.
  94. var childpos = 2 * pos + 1 # leftmost child position
  95. while childpos < endpos:
  96. # Set childpos to index of smaller child.
  97. let rightpos = childpos + 1
  98. if rightpos < endpos and not heapCmp(heap[childpos], heap[rightpos]):
  99. childpos = rightpos
  100. # Move the smaller child up.
  101. heap.data[pos] = heap[childpos]
  102. pos = childpos
  103. childpos = 2 * pos + 1
  104. # The leaf at pos is empty now. Put newitem there, and bubble it up
  105. # to its final resting place (by sifting its parents down).
  106. heap.data[pos] = newitem
  107. siftup(heap, startpos, pos)
  108. proc siftdown[T](heap: var HeapQueue[T], p: int) =
  109. let endpos = len(heap)
  110. var pos = p
  111. let newitem = heap[pos]
  112. var childpos = 2 * pos + 1
  113. while childpos < endpos:
  114. let rightpos = childpos + 1
  115. if rightpos < endpos and not heapCmp(heap[childpos], heap[rightpos]):
  116. childpos = rightpos
  117. if not heapCmp(heap[childpos], newitem):
  118. break
  119. heap.data[pos] = heap[childpos]
  120. pos = childpos
  121. childpos = 2 * pos + 1
  122. heap.data[pos] = newitem
  123. proc push*[T](heap: var HeapQueue[T], item: sink T) =
  124. ## Pushes `item` onto `heap`, maintaining the heap invariant.
  125. heap.data.add(item)
  126. siftup(heap, 0, len(heap) - 1)
  127. proc toHeapQueue*[T](x: openArray[T]): HeapQueue[T] {.since: (1, 3).} =
  128. ## Creates a new HeapQueue that contains the elements of `x`.
  129. ##
  130. ## **See also:**
  131. ## * `initHeapQueue proc <#initHeapQueue>`_
  132. runnableExamples:
  133. var heap = [9, 5, 8].toHeapQueue
  134. assert heap.pop() == 5
  135. assert heap[0] == 8
  136. # see https://en.wikipedia.org/wiki/Binary_heap#Building_a_heap
  137. result.data = @x
  138. for i in countdown(x.len div 2 - 1, 0):
  139. siftdown(result, i)
  140. proc pop*[T](heap: var HeapQueue[T]): T =
  141. ## Pops and returns the smallest item from `heap`,
  142. ## maintaining the heap invariant.
  143. runnableExamples:
  144. var heap = [9, 5, 8].toHeapQueue
  145. assert heap.pop() == 5
  146. let lastelt = heap.data.pop()
  147. if heap.len > 0:
  148. result = heap[0]
  149. heap.data[0] = lastelt
  150. siftdownToBottom(heap, 0)
  151. else:
  152. result = lastelt
  153. proc find*[T](heap: HeapQueue[T], x: T): int {.since: (1, 3).} =
  154. ## Linear scan to find the index of the item `x` or -1 if not found.
  155. runnableExamples:
  156. let heap = [9, 5, 8].toHeapQueue
  157. assert heap.find(5) == 0
  158. assert heap.find(9) == 1
  159. assert heap.find(777) == -1
  160. result = -1
  161. for i in 0 ..< heap.len:
  162. if heap[i] == x: return i
  163. proc contains*[T](heap: HeapQueue[T], x: T): bool {.since: (2, 1, 1).} =
  164. ## Returns true if `x` is in `heap` or false if not found. This is a shortcut
  165. ## for `find(heap, x) >= 0`.
  166. result = find(heap, x) >= 0
  167. proc del*[T](heap: var HeapQueue[T], index: Natural) =
  168. ## Removes the element at `index` from `heap`, maintaining the heap invariant.
  169. runnableExamples:
  170. var heap = [9, 5, 8].toHeapQueue
  171. heap.del(1)
  172. assert heap[0] == 5
  173. assert heap[1] == 8
  174. swap(heap.data[^1], heap.data[index])
  175. let newLen = heap.len - 1
  176. heap.data.setLen(newLen)
  177. if index < newLen:
  178. siftdownToBottom(heap, index)
  179. proc replace*[T](heap: var HeapQueue[T], item: sink T): T =
  180. ## Pops and returns the current smallest value, and add the new item.
  181. ## This is more efficient than `pop()` followed by `push()`, and can be
  182. ## more appropriate when using a fixed-size heap. Note that the value
  183. ## returned may be larger than `item`! That constrains reasonable uses of
  184. ## this routine unless written as part of a conditional replacement.
  185. ##
  186. ## **See also:**
  187. ## * `pushpop proc <#pushpop,HeapQueue[T],sinkT>`_
  188. runnableExamples:
  189. var heap = [5, 12].toHeapQueue
  190. assert heap.replace(6) == 5
  191. assert heap.len == 2
  192. assert heap[0] == 6
  193. assert heap.replace(4) == 6
  194. result = heap[0]
  195. heap.data[0] = item
  196. siftdown(heap, 0)
  197. proc pushpop*[T](heap: var HeapQueue[T], item: sink T): T =
  198. ## Fast version of a `push()` followed by a `pop()`.
  199. ##
  200. ## **See also:**
  201. ## * `replace proc <#replace,HeapQueue[T],sinkT>`_
  202. runnableExamples:
  203. var heap = [5, 12].toHeapQueue
  204. assert heap.pushpop(6) == 5
  205. assert heap.len == 2
  206. assert heap[0] == 6
  207. assert heap.pushpop(4) == 4
  208. result = item
  209. if heap.len > 0 and heapCmp(heap.data[0], result):
  210. swap(result, heap.data[0])
  211. siftdown(heap, 0)
  212. proc clear*[T](heap: var HeapQueue[T]) =
  213. ## Removes all elements from `heap`, making it empty.
  214. runnableExamples:
  215. var heap = [9, 5, 8].toHeapQueue
  216. heap.clear()
  217. assert heap.len == 0
  218. heap.data.setLen(0)
  219. proc `$`*[T](heap: HeapQueue[T]): string =
  220. ## Turns a heap into its string representation.
  221. runnableExamples:
  222. let heap = [1, 2].toHeapQueue
  223. assert $heap == "[1, 2]"
  224. result = "["
  225. for x in heap.data:
  226. if result.len > 1: result.add(", ")
  227. result.addQuoted(x)
  228. result.add("]")