heapqueue.nim 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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. ##[ Heap queue algorithm (a.k.a. priority queue). Ported from Python heapq.
  9. Heaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for
  10. all k, counting elements from 0. For the sake of comparison,
  11. non-existing elements are considered to be infinite. The interesting
  12. property of a heap is that a[0] is always its smallest element.
  13. ]##
  14. type HeapQueue*[T] = distinct seq[T]
  15. proc newHeapQueue*[T](): HeapQueue[T] {.inline.} = HeapQueue[T](newSeq[T]())
  16. proc newHeapQueue*[T](h: var HeapQueue[T]) {.inline.} = h = HeapQueue[T](newSeq[T]())
  17. proc len*[T](h: HeapQueue[T]): int {.inline.} = seq[T](h).len
  18. proc `[]`*[T](h: HeapQueue[T], i: int): T {.inline.} = seq[T](h)[i]
  19. proc `[]=`[T](h: var HeapQueue[T], i: int, v: T) {.inline.} = seq[T](h)[i] = v
  20. proc add[T](h: var HeapQueue[T], v: T) {.inline.} = seq[T](h).add(v)
  21. proc heapCmp[T](x, y: T): bool {.inline.} =
  22. return (x < y)
  23. # 'heap' is a heap at all indices >= startpos, except possibly for pos. pos
  24. # is the index of a leaf with a possibly out-of-order value. Restore the
  25. # heap invariant.
  26. proc siftdown[T](heap: var HeapQueue[T], startpos, p: int) =
  27. var pos = p
  28. var newitem = heap[pos]
  29. # Follow the path to the root, moving parents down until finding a place
  30. # newitem fits.
  31. while pos > startpos:
  32. let parentpos = (pos - 1) shr 1
  33. let parent = heap[parentpos]
  34. if heapCmp(newitem, parent):
  35. heap[pos] = parent
  36. pos = parentpos
  37. else:
  38. break
  39. heap[pos] = newitem
  40. proc siftup[T](heap: var HeapQueue[T], p: int) =
  41. let endpos = len(heap)
  42. var pos = p
  43. let startpos = pos
  44. let newitem = heap[pos]
  45. # Bubble up the smaller child until hitting a leaf.
  46. var childpos = 2*pos + 1 # leftmost child position
  47. while childpos < endpos:
  48. # Set childpos to index of smaller child.
  49. let rightpos = childpos + 1
  50. if rightpos < endpos and not heapCmp(heap[childpos], heap[rightpos]):
  51. childpos = rightpos
  52. # Move the smaller child up.
  53. heap[pos] = heap[childpos]
  54. pos = childpos
  55. childpos = 2*pos + 1
  56. # The leaf at pos is empty now. Put newitem there, and bubble it up
  57. # to its final resting place (by sifting its parents down).
  58. heap[pos] = newitem
  59. siftdown(heap, startpos, pos)
  60. proc push*[T](heap: var HeapQueue[T], item: T) =
  61. ## Push item onto heap, maintaining the heap invariant.
  62. (seq[T](heap)).add(item)
  63. siftdown(heap, 0, len(heap)-1)
  64. proc pop*[T](heap: var HeapQueue[T]): T =
  65. ## Pop the smallest item off the heap, maintaining the heap invariant.
  66. let lastelt = seq[T](heap).pop()
  67. if heap.len > 0:
  68. result = heap[0]
  69. heap[0] = lastelt
  70. siftup(heap, 0)
  71. else:
  72. result = lastelt
  73. proc del*[T](heap: var HeapQueue[T], index: int) =
  74. ## Removes element at `index`, maintaining the heap invariant.
  75. swap(seq[T](heap)[^1], seq[T](heap)[index])
  76. let newLen = heap.len - 1
  77. seq[T](heap).setLen(newLen)
  78. if index < newLen:
  79. heap.siftup(index)
  80. proc replace*[T](heap: var HeapQueue[T], item: T): T =
  81. ## Pop and return the current smallest value, and add the new item.
  82. ## This is more efficient than pop() followed by push(), and can be
  83. ## more appropriate when using a fixed-size heap. Note that the value
  84. ## returned may be larger than item! That constrains reasonable uses of
  85. ## this routine unless written as part of a conditional replacement:
  86. ## if item > heap[0]:
  87. ## item = replace(heap, item)
  88. result = heap[0]
  89. heap[0] = item
  90. siftup(heap, 0)
  91. proc pushpop*[T](heap: var HeapQueue[T], item: T): T =
  92. ## Fast version of a push followed by a pop.
  93. if heap.len > 0 and heapCmp(heap[0], item):
  94. swap(item, heap[0])
  95. siftup(heap, 0)
  96. return item
  97. when isMainModule:
  98. proc toSortedSeq[T](h: HeapQueue[T]): seq[T] =
  99. var tmp = h
  100. result = @[]
  101. while tmp.len > 0:
  102. result.add(pop(tmp))
  103. block: # Simple sanity test
  104. var heap = newHeapQueue[int]()
  105. let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
  106. for item in data:
  107. push(heap, item)
  108. doAssert(heap[0] == 0)
  109. doAssert(heap.toSortedSeq == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
  110. block: # Test del
  111. var heap = newHeapQueue[int]()
  112. let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
  113. for item in data: push(heap, item)
  114. heap.del(0)
  115. doAssert(heap[0] == 1)
  116. heap.del(seq[int](heap).find(7))
  117. doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 5, 6, 8, 9])
  118. heap.del(seq[int](heap).find(5))
  119. doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 6, 8, 9])
  120. heap.del(seq[int](heap).find(6))
  121. doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 8, 9])
  122. heap.del(seq[int](heap).find(2))
  123. doAssert(heap.toSortedSeq == @[1, 3, 4, 8, 9])
  124. block: # Test del last
  125. var heap = newHeapQueue[int]()
  126. let data = [1, 2, 3]
  127. for item in data: push(heap, item)
  128. heap.del(2)
  129. doAssert(heap.toSortedSeq == @[1, 2])
  130. heap.del(1)
  131. doAssert(heap.toSortedSeq == @[1])
  132. heap.del(0)
  133. doAssert(heap.toSortedSeq == @[])