github.com/fluhus/gostuff@v0.4.1-0.20240331134726-be71864f2b5d/heaps/heaps.go (about) 1 // Package heaps provides generic heaps. 2 // 3 // This package provides better run speeds than the standard [heap] package. 4 package heaps 5 6 import "golang.org/x/exp/constraints" 7 8 // Heap is a generic heap. 9 type Heap[T any] struct { 10 a []T 11 less func(T, T) bool 12 } 13 14 // New returns a new heap that uses the given comparator function. 15 func New[T any](less func(T, T) bool) *Heap[T] { 16 return &Heap[T]{nil, less} 17 } 18 19 // Min returns a new min-heap of an ordered type by its natural order. 20 func Min[T constraints.Ordered]() *Heap[T] { 21 return New(func(t1, t2 T) bool { 22 return t1 < t2 23 }) 24 } 25 26 // Max returns a new max-heap of an ordered type by its natural order. 27 func Max[T constraints.Ordered]() *Heap[T] { 28 return New(func(t1, t2 T) bool { 29 return t1 > t2 30 }) 31 } 32 33 // Push adds x to h while maintaining its heap invariants. 34 func (h *Heap[T]) Push(x T) { 35 h.a = append(h.a, x) 36 i := len(h.a) - 1 37 for i != -1 { 38 i = h.bubbleUp(i) 39 } 40 } 41 42 // PushSlice adds the elements of s to h while maintaining its heap invariants. 43 // The complexity is O(new n), so it should be typically used to initialize a 44 // new heap. 45 func (h *Heap[T]) PushSlice(s []T) { 46 h.a = append(h.a, s...) 47 for i := len(h.a) - 1; i >= 0; i-- { 48 j := i 49 for j != -1 { 50 j = h.bubbleDown(j) 51 } 52 } 53 } 54 55 // Pop removes and returns the minimal element in h. 56 func (h *Heap[T]) Pop() T { 57 if len(h.a) == 0 { 58 panic("called Pop() on an empty heap") 59 } 60 x := h.a[0] 61 h.a[0] = h.a[len(h.a)-1] 62 h.a = h.a[:len(h.a)-1] 63 i := 0 64 for i != -1 { 65 i = h.bubbleDown(i) 66 } 67 // Shrink if needed. 68 if cap(h.a) >= 16 && len(h.a) <= cap(h.a)/4 { 69 h.a = append(make([]T, 0, cap(h.a)/2), h.a...) 70 } 71 return x 72 } 73 74 // Len returns the number of elements in h. 75 func (h *Heap[T]) Len() int { 76 return len(h.a) 77 } 78 79 // Moves the i'th element down and returns its new index. 80 // Returns -1 when no more bubble-downs are needed. 81 func (h *Heap[T]) bubbleDown(i int) int { 82 ia, ib := i*2+1, i*2+2 83 if len(h.a) < ib { // No children 84 return -1 85 } 86 if len(h.a) == ib { // Only one child 87 if h.less(h.a[ia], h.a[i]) { 88 h.a[i], h.a[ia] = h.a[ia], h.a[i] 89 } 90 return -1 91 } 92 if h.less(h.a[ib], h.a[ia]) { 93 ia = ib 94 } 95 if h.less(h.a[ia], h.a[i]) { 96 h.a[i], h.a[ia] = h.a[ia], h.a[i] 97 } 98 return ia 99 } 100 101 // Moves the i'th element up and returns its new index. 102 // Returns -1 when no more bubble-ups are needed. 103 func (h *Heap[T]) bubbleUp(i int) int { 104 if i == 0 { 105 return -1 106 } 107 ia := (i - 1) / 2 108 if !h.less(h.a[i], h.a[ia]) { 109 return -1 110 } 111 h.a[i], h.a[ia] = h.a[ia], h.a[i] 112 return ia 113 } 114 115 // View returns the underlying slice of h, containing all of its elements. 116 // Modifying the slice may invalidate the heap. 117 func (h *Heap[T]) View() []T { 118 return h.a 119 } 120 121 // Head returns the minimal element in h. 122 func (h *Heap[T]) Head() T { 123 return h.a[0] 124 } 125 126 // Fix fixes the heap after a single value had been modified. 127 // i is the index of the modified value. 128 func (h *Heap[T]) Fix(i int) { 129 wentUp := false 130 for { 131 j := h.bubbleUp(i) 132 if j == -1 { 133 break 134 } 135 i = j 136 wentUp = true 137 } 138 if !wentUp { 139 for { 140 j := h.bubbleDown(i) 141 if j == -1 { 142 break 143 } 144 i = j 145 } 146 } 147 }