heapqueue.nim 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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. ##[
  9. The `heapqueue` module implements a
  10. `heap data structure<https://en.wikipedia.org/wiki/Heap_(data_structure)>`_
  11. that can be used as a
  12. `priority queue<https://en.wikipedia.org/wiki/Priority_queue>`_.
  13. Heaps are arrays for which `a[k] <= a[2*k+1]` and `a[k] <= a[2*k+2]` for
  14. all `k`, counting elements from 0. The interesting property of a heap is that
  15. `a[0]` is always its smallest element.
  16. Basic usage
  17. -----------
  18. .. code-block:: Nim
  19. import heapqueue
  20. var heap = initHeapQueue[int]()
  21. heap.push(8)
  22. heap.push(2)
  23. heap.push(5)
  24. # The first element is the lowest element
  25. assert heap[0] == 2
  26. # Remove and return the lowest element
  27. assert heap.pop() == 2
  28. # The lowest element remaining is 5
  29. assert heap[0] == 5
  30. Usage with custom object
  31. ------------------------
  32. To use a `HeapQueue` with a custom object, the `<` operator must be
  33. implemented.
  34. .. code-block:: Nim
  35. import heapqueue
  36. type Job = object
  37. priority: int
  38. proc `<`(a, b: Job): bool = a.priority < b.priority
  39. var jobs = initHeapQueue[Job]()
  40. jobs.push(Job(priority: 1))
  41. jobs.push(Job(priority: 2))
  42. assert jobs[0].priority == 1
  43. ]##
  44. type HeapQueue*[T] = object
  45. ## A heap queue, commonly known as a priority queue.
  46. data: seq[T]
  47. proc initHeapQueue*[T](): HeapQueue[T] =
  48. ## Create a new empty heap.
  49. discard
  50. proc len*[T](heap: HeapQueue[T]): int {.inline.} =
  51. ## Return the number of elements of `heap`.
  52. heap.data.len
  53. proc `[]`*[T](heap: HeapQueue[T], i: Natural): T {.inline.} =
  54. ## Access the i-th element of `heap`.
  55. heap.data[i]
  56. proc heapCmp[T](x, y: T): bool {.inline.} =
  57. return (x < y)
  58. proc siftdown[T](heap: var HeapQueue[T], startpos, p: int) =
  59. ## 'heap' is a heap at all indices >= startpos, except possibly for pos. pos
  60. ## is the index of a leaf with a possibly out-of-order value. Restore the
  61. ## heap invariant.
  62. var pos = p
  63. var newitem = heap[pos]
  64. # Follow the path to the root, moving parents down until finding a place
  65. # newitem fits.
  66. while pos > startpos:
  67. let parentpos = (pos - 1) shr 1
  68. let parent = heap[parentpos]
  69. if heapCmp(newitem, parent):
  70. heap.data[pos] = parent
  71. pos = parentpos
  72. else:
  73. break
  74. heap.data[pos] = newitem
  75. proc siftup[T](heap: var HeapQueue[T], p: int) =
  76. let endpos = len(heap)
  77. var pos = p
  78. let startpos = pos
  79. let newitem = heap[pos]
  80. # Bubble up the smaller child until hitting a leaf.
  81. var childpos = 2*pos + 1 # leftmost child position
  82. while childpos < endpos:
  83. # Set childpos to index of smaller child.
  84. let rightpos = childpos + 1
  85. if rightpos < endpos and not heapCmp(heap[childpos], heap[rightpos]):
  86. childpos = rightpos
  87. # Move the smaller child up.
  88. heap.data[pos] = heap[childpos]
  89. pos = childpos
  90. childpos = 2*pos + 1
  91. # The leaf at pos is empty now. Put newitem there, and bubble it up
  92. # to its final resting place (by sifting its parents down).
  93. heap.data[pos] = newitem
  94. siftdown(heap, startpos, pos)
  95. proc push*[T](heap: var HeapQueue[T], item: T) =
  96. ## Push `item` onto heap, maintaining the heap invariant.
  97. heap.data.add(item)
  98. siftdown(heap, 0, len(heap)-1)
  99. proc pop*[T](heap: var HeapQueue[T]): T =
  100. ## Pop and return the smallest item from `heap`,
  101. ## maintaining the heap invariant.
  102. let lastelt = heap.data.pop()
  103. if heap.len > 0:
  104. result = heap[0]
  105. heap.data[0] = lastelt
  106. siftup(heap, 0)
  107. else:
  108. result = lastelt
  109. proc del*[T](heap: var HeapQueue[T], index: Natural) =
  110. ## Removes the element at `index` from `heap`, maintaining the heap invariant.
  111. swap(heap.data[^1], heap.data[index])
  112. let newLen = heap.len - 1
  113. heap.data.setLen(newLen)
  114. if index < newLen:
  115. heap.siftup(index)
  116. proc replace*[T](heap: var HeapQueue[T], item: T): T =
  117. ## Pop and return the current smallest value, and add the new item.
  118. ## This is more efficient than pop() followed by push(), and can be
  119. ## more appropriate when using a fixed-size heap. Note that the value
  120. ## returned may be larger than item! That constrains reasonable uses of
  121. ## this routine unless written as part of a conditional replacement:
  122. ##
  123. ## .. code-block:: nim
  124. ## if item > heap[0]:
  125. ## item = replace(heap, item)
  126. result = heap[0]
  127. heap.data[0] = item
  128. siftup(heap, 0)
  129. proc pushpop*[T](heap: var HeapQueue[T], item: T): T =
  130. ## Fast version of a push followed by a pop.
  131. if heap.len > 0 and heapCmp(heap[0], item):
  132. swap(item, heap[0])
  133. siftup(heap, 0)
  134. return item
  135. proc clear*[T](heap: var HeapQueue[T]) =
  136. ## Remove all elements from `heap`, making it empty.
  137. runnableExamples:
  138. var heap = initHeapQueue[int]()
  139. heap.push(1)
  140. heap.clear()
  141. assert heap.len == 0
  142. heap.data.setLen(0)
  143. proc `$`*[T](heap: HeapQueue[T]): string =
  144. ## Turn a heap into its string representation.
  145. runnableExamples:
  146. var heap = initHeapQueue[int]()
  147. heap.push(1)
  148. heap.push(2)
  149. assert $heap == "[1, 2]"
  150. result = "["
  151. for x in heap.data:
  152. if result.len > 1: result.add(", ")
  153. result.addQuoted(x)
  154. result.add("]")
  155. proc newHeapQueue*[T](): HeapQueue[T] {.deprecated:
  156. "Deprecated since v0.20.0: use 'initHeapQueue' instead.".} =
  157. initHeapQueue[T]()
  158. proc newHeapQueue*[T](heap: var HeapQueue[T]) {.deprecated:
  159. "Deprecated since v0.20.0: use 'clear' instead.".} =
  160. heap.clear()
  161. when isMainModule:
  162. proc toSortedSeq[T](h: HeapQueue[T]): seq[T] =
  163. var tmp = h
  164. result = @[]
  165. while tmp.len > 0:
  166. result.add(pop(tmp))
  167. block: # Simple sanity test
  168. var heap = initHeapQueue[int]()
  169. let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
  170. for item in data:
  171. push(heap, item)
  172. doAssert(heap[0] == 0)
  173. doAssert(heap.toSortedSeq == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
  174. block: # Test del
  175. var heap = initHeapQueue[int]()
  176. let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
  177. for item in data: push(heap, item)
  178. heap.del(0)
  179. doAssert(heap[0] == 1)
  180. heap.del(heap.data.find(7))
  181. doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 5, 6, 8, 9])
  182. heap.del(heap.data.find(5))
  183. doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 6, 8, 9])
  184. heap.del(heap.data.find(6))
  185. doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 8, 9])
  186. heap.del(heap.data.find(2))
  187. doAssert(heap.toSortedSeq == @[1, 3, 4, 8, 9])
  188. block: # Test del last
  189. var heap = initHeapQueue[int]()
  190. let data = [1, 2, 3]
  191. for item in data: push(heap, item)
  192. heap.del(2)
  193. doAssert(heap.toSortedSeq == @[1, 2])
  194. heap.del(1)
  195. doAssert(heap.toSortedSeq == @[1])
  196. heap.del(0)
  197. doAssert(heap.toSortedSeq == @[])