github.com/songzhibin97/go-baseutils@v0.0.2-0.20240302024150-487d8ce9c082/structure/trees/binaryheap/binaryheap.go (about) 1 // Package binaryheap implements a binary heap backed by array list. 2 // 3 // Comparator defines this heap as either min or max heap. 4 // 5 // Structure is not thread safe. 6 // 7 // References: http://en.wikipedia.org/wiki/Binary_heap 8 package binaryheap 9 10 import ( 11 "fmt" 12 "github.com/songzhibin97/go-baseutils/base/bcomparator" 13 "github.com/songzhibin97/go-baseutils/structure/lists/arraylist" 14 "github.com/songzhibin97/go-baseutils/structure/trees" 15 16 "strings" 17 ) 18 19 // Assert Tree implementation 20 var _ trees.Tree[any] = (*Heap[any])(nil) 21 22 // Heap holds elements in an array-list 23 type Heap[E any] struct { 24 list *arraylist.List[E] 25 Comparator bcomparator.Comparator[E] 26 } 27 28 // NewWith instantiates a new empty heap tree with the custom comparator. 29 func NewWith[E any](comparator bcomparator.Comparator[E]) *Heap[E] { 30 return &Heap[E]{list: arraylist.New[E](), Comparator: comparator} 31 } 32 33 // NewWithIntComparator instantiates a new empty heap with the IntComparator, i.e. elements are of type int. 34 func NewWithIntComparator() *Heap[int] { 35 return &Heap[int]{list: arraylist.New[int](), Comparator: bcomparator.IntComparator()} 36 } 37 38 // NewWithStringComparator instantiates a new empty heap with the StringComparator, i.e. elements are of type string. 39 func NewWithStringComparator() *Heap[string] { 40 return &Heap[string]{list: arraylist.New[string](), Comparator: bcomparator.StringComparator()} 41 } 42 43 // Push adds a value onto the heap and bubbles it up accordingly. 44 func (heap *Heap[E]) Push(values ...E) { 45 if len(values) == 1 { 46 heap.list.Add(values[0]) 47 heap.bubbleUp() 48 } else { 49 // Reference: https://en.wikipedia.org/wiki/Binary_heap#Building_a_heap 50 for _, value := range values { 51 heap.list.Add(value) 52 } 53 size := heap.list.Size()/2 + 1 54 for i := size; i >= 0; i-- { 55 heap.bubbleDownIndex(i) 56 } 57 } 58 } 59 60 // Pop removes top element on heap and returns it, or nil if heap is empty. 61 // Second return parameter is true, unless the heap was empty and there was nothing to pop. 62 func (heap *Heap[E]) Pop() (value E, ok bool) { 63 value, ok = heap.list.Get(0) 64 if !ok { 65 return 66 } 67 lastIndex := heap.list.Size() - 1 68 heap.list.Swap(0, lastIndex) 69 heap.list.Remove(lastIndex) 70 heap.bubbleDown() 71 return 72 } 73 74 // Peek returns top element on the heap without removing it, or nil if heap is empty. 75 // Second return parameter is true, unless the heap was empty and there was nothing to peek. 76 func (heap *Heap[E]) Peek() (value E, ok bool) { 77 return heap.list.Get(0) 78 } 79 80 // Empty returns true if heap does not contain any elements. 81 func (heap *Heap[E]) Empty() bool { 82 return heap.list.Empty() 83 } 84 85 // Size returns number of elements within the heap. 86 func (heap *Heap[E]) Size() int { 87 return heap.list.Size() 88 } 89 90 // Clear removes all elements from the heap. 91 func (heap *Heap[E]) Clear() { 92 heap.list.Clear() 93 } 94 95 // Values returns all elements in the heap. 96 func (heap *Heap[E]) Values() []E { 97 values := make([]E, heap.list.Size(), heap.list.Size()) 98 for it := heap.Iterator(); it.Next(); { 99 values[it.Index()] = it.Value() 100 } 101 return values 102 } 103 104 // String returns a string representation of container 105 func (heap *Heap[E]) String() string { 106 b := strings.Builder{} 107 b.WriteString("BinaryHeap\n") 108 for it := heap.Iterator(); it.Next(); { 109 b.WriteString(fmt.Sprintf("(value:%v) ", it.Value())) 110 } 111 return b.String() 112 } 113 114 // Performs the "bubble down" operation. This is to place the element that is at the root 115 // of the heap in its correct place so that the heap maintains the min/max-heap order property. 116 func (heap *Heap[E]) bubbleDown() { 117 heap.bubbleDownIndex(0) 118 } 119 120 // Performs the "bubble down" operation. This is to place the element that is at the index 121 // of the heap in its correct place so that the heap maintains the min/max-heap order property. 122 func (heap *Heap[E]) bubbleDownIndex(index int) { 123 size := heap.list.Size() 124 for leftIndex := index<<1 + 1; leftIndex < size; leftIndex = index<<1 + 1 { 125 rightIndex := index<<1 + 2 126 smallerIndex := leftIndex 127 leftValue, _ := heap.list.Get(leftIndex) 128 rightValue, _ := heap.list.Get(rightIndex) 129 if rightIndex < size && heap.Comparator(leftValue, rightValue) > 0 { 130 smallerIndex = rightIndex 131 } 132 indexValue, _ := heap.list.Get(index) 133 smallerValue, _ := heap.list.Get(smallerIndex) 134 if heap.Comparator(indexValue, smallerValue) > 0 { 135 heap.list.Swap(index, smallerIndex) 136 } else { 137 break 138 } 139 index = smallerIndex 140 } 141 } 142 143 // Performs the "bubble up" operation. This is to place a newly inserted 144 // element (i.e. last element in the list) in its correct place so that 145 // the heap maintains the min/max-heap order property. 146 func (heap *Heap[E]) bubbleUp() { 147 index := heap.list.Size() - 1 148 for parentIndex := (index - 1) >> 1; index > 0; parentIndex = (index - 1) >> 1 { 149 indexValue, _ := heap.list.Get(index) 150 parentValue, _ := heap.list.Get(parentIndex) 151 if heap.Comparator(parentValue, indexValue) <= 0 { 152 break 153 } 154 heap.list.Swap(index, parentIndex) 155 index = parentIndex 156 } 157 } 158 159 // Check that the index is within bounds of the list 160 func (heap *Heap[E]) withinRange(index int) bool { 161 return index >= 0 && index < heap.list.Size() 162 } 163 164 // UnmarshalJSON @implements json.Unmarshaler 165 func (heap *Heap[E]) UnmarshalJSON(bytes []byte) error { 166 return heap.list.UnmarshalJSON(bytes) 167 } 168 169 // MarshalJSON @implements json.Marshaler 170 func (heap *Heap[E]) MarshalJSON() ([]byte, error) { 171 return heap.list.MarshalJSON() 172 }