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  }