heap.odin 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /*
  2. Copyright 2022 Dale Weiler <[email protected]>.
  3. Made available under Odin's BSD-3 license.
  4. List of contributors:
  5. Dale Weiler: Initial implementation
  6. */
  7. // Package implements a generic max heap in-place on a slice for any type.
  8. package heap
  9. /*
  10. Constructs a max heap in slice given by data with comparator. A max heap is
  11. a range of elements which has the following properties:
  12. 1. With N = len(data), for all 0 < i < N, data[(i - 1) / 2] does not compare
  13. less than data[i].
  14. 2. A new element can be added using push in O(log n) time.
  15. 3. The first element can be removed using pop in O(log n) time.
  16. The comparator compares elements of type T and can be used to construct a
  17. max heap (less than) or min heap (greater than) for T.
  18. */
  19. make :: proc(data: []$T, less: proc(a, b: T) -> bool) {
  20. // amoritize length lookup
  21. length := len(data)
  22. if length <= 1 {
  23. return
  24. }
  25. // start from data parent, no need to consider children
  26. for start := (length - 2) / 2; start >= 0; start -= 1 {
  27. sift_down(data, less, start)
  28. }
  29. }
  30. /*
  31. Inserts the element at the position len(data)-1 into the max heap with
  32. comparator.
  33. At most log(N) comparisons where N = len(data) will be performed.
  34. */
  35. push :: proc(data: []$T, less: proc(a, b: T) -> bool) {
  36. sift_up(data, less)
  37. }
  38. /*
  39. Swaps the value in position data[0] and the value in data[len(data)-1] and
  40. makes subrange [0, len(data)-1) into a heap. This has the effect of removing
  41. the first element from the heap.
  42. At most 2 * log(N) comparisons where N = len(data) will be performed.
  43. */
  44. pop :: proc(data: []$T, less: proc(a, b: T) -> bool) {
  45. length := len(data)
  46. if length <= 1 {
  47. return
  48. }
  49. last := length
  50. // create a hole at 0
  51. top := data[0]
  52. hole := floyd_sift_down(data, less)
  53. last -= 1
  54. if hole == last {
  55. data[hole] = top
  56. } else {
  57. data[hole] = data[last]
  58. hole += 1
  59. data[last] = top
  60. sift_up(data[:hole], less)
  61. }
  62. }
  63. /*
  64. Converts the max heap into a sorted range in ascending order. The resulting
  65. slice will no longer be a heap after this.
  66. At most 2 * N * log(N) comparisons where N = len(data) will be performed.
  67. */
  68. sort :: proc(data: []$T, less: proc(a, b: T) -> bool) {
  69. for n := len(data); n >= 1; n -= 1 {
  70. pop(data[:n], less)
  71. }
  72. }
  73. /*
  74. Examines the slice and finds the largest range which is a max-heap. Elements
  75. are compared with user-supplied comparison procedure.
  76. This returns the upper bound of the largest range in the slice which is a
  77. max heap. That is, the last index for which data is a max heap.
  78. At most O(n) comparisons where N = len(data) will be performed.
  79. */
  80. is_heap_until :: proc(data: []$T, less: proc(a, b: T) -> bool) -> int {
  81. length := len(data)
  82. a := 0
  83. b := 1
  84. for b < length {
  85. if less(data[a], data[b]) {
  86. return b
  87. }
  88. b += 1
  89. if b == length || less(data[a], data[b]) {
  90. return b
  91. }
  92. a += 1
  93. b = 2 * a + 1
  94. }
  95. return length
  96. }
  97. /*
  98. Checks if a given slice is a max heap.
  99. At most O(n) comparisons where N = len(data) will be performed.
  100. */
  101. is_heap :: #force_inline proc(data: []$T, less: proc(a, b: T) -> bool) -> bool {
  102. return is_heap_until(data, less) == len(data)
  103. }
  104. @(private="file")
  105. floyd_sift_down :: proc(data: []$T, less: proc(a, b: T) -> bool) -> int {
  106. length := len(data)
  107. assert(length >= 2)
  108. hole := 0
  109. child := 0
  110. index := 0
  111. for {
  112. index += child + 1
  113. child = 2 * child + 1
  114. if child + 1 < length && less(data[index], data[index + 1]) {
  115. child += 1
  116. index += 1
  117. }
  118. data[hole] = data[index]
  119. hole = index
  120. if child > (length - 2) / 2 {
  121. return hole
  122. }
  123. }
  124. unreachable()
  125. }
  126. @(private="file")
  127. sift_down :: proc(data: []$T, less: proc(a, b: T) -> bool, start: int) {
  128. start := start
  129. child := start
  130. // amoritize length lookup
  131. length := len(data)
  132. // left child of start is at 2 * start + 1
  133. // right child of start is at 2 * start + 2
  134. if length < 2 || (length - 2) / 2 < child {
  135. return
  136. }
  137. child = 2 * child + 1
  138. if child + 1 < length && less(data[child], data[child + 1]) {
  139. // right child exists and is greater than left child
  140. child += 1
  141. }
  142. // check if in heap order
  143. if less(data[child], data[start]) {
  144. // start is larger than its largest child
  145. return
  146. }
  147. top := data[start]
  148. for {
  149. // not in heap order, swap parent with its largest child
  150. data[start] = data[child]
  151. start = child
  152. if (length - 2) / 2 < child {
  153. break
  154. }
  155. // recompute child based off updated parent
  156. child = 2 * child + 1
  157. if child + 1 < length && less(data[child], data[child + 1]) {
  158. // right child exists and is greater than left child
  159. child += 1
  160. }
  161. // check if we are in heap order
  162. if less(data[child], top) {
  163. break
  164. }
  165. }
  166. data[start] = top
  167. }
  168. @(private="file")
  169. sift_up :: proc(data: []$T, less: proc(a, b: T) -> bool) {
  170. // amoritize length lookup
  171. length := len(data)
  172. if length <= 1 {
  173. return
  174. }
  175. last := length
  176. length = (length - 2) / 2
  177. index := length
  178. last -= 1
  179. if less(data[index], data[last]) {
  180. top := data[last]
  181. for {
  182. data[last] = data[index]
  183. last = index
  184. if length == 0 {
  185. break
  186. }
  187. length = (length - 1) / 2
  188. index = length
  189. if !less(data[index], top) {
  190. break
  191. }
  192. }
  193. data[last] = top
  194. }
  195. }