-
Notifications
You must be signed in to change notification settings - Fork 0
/
binary_heap.go
288 lines (250 loc) · 8.47 KB
/
binary_heap.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
package gost
// A priority queue implemented with a binary heap.
// This will be a max-heap.
// It is a logic error for an item to be modified in such a way that the item’s ordering relative to any other item, as determined by the Ord trait, changes while it is in the heap. This is normally only possible through interior mutability, global state, I/O, or unsafe code. The behavior resulting from such a logic error is not specified, but will be encapsulated to the BinaryHeap that observed the logic error and not result in undefined behavior. This could include panics, incorrect results, aborts, memory leaks, and non-termination.
// As long as no elements change their relative order while being in the heap as described above, the API of BinaryHeap guarantees that the heap invariant remains intact i.e. its methods all behave as documented. For example if a method is documented as iterating in sorted order, that’s guaranteed to work as long as elements in the heap have not changed order, even in the presence of closures getting unwinded out of, iterators getting leaked, and similar foolishness.
type BinaryHeap[T Ord[T]] struct {
vec Vec[T]
}
// Constructs a new, empty BinaryHeapNew<T>.
func BinaryHeapNew[T Ord[T]]() BinaryHeap[T] {
return BinaryHeap[T]{
vec: VecNew[T](),
}
}
// Constructs a new, empty BinaryHeap<T> with at least the specified capacity.
func BinaryHeapWithCapacity[T Ord[T]](capacity USize) BinaryHeap[T] {
return BinaryHeap[T]{
vec: VecWithCapacity[T](capacity),
}
}
// Constructs a new, empty BinaryHeap<T> with at least the specified len.
func BinaryHeapWithLen[T Ord[T]](len USize) BinaryHeap[T] {
return BinaryHeap[T]{
vec: VecWithLen[T](len),
}
}
// Returns the total number of elements the heap can hold without reallocating.
//
// heap := gost.BinaryHeapNew[gost.I32]()
// gost.AssertEq(heap.Capacity(), gost.USize(0))
//
// heap.Reserve(10)
// gost.AssertEq(heap.Capacity(), gost.USize(10))
func (self BinaryHeap[T]) Capacity() USize {
return self.vec.Capacity()
}
// Returns the number of elements in the heap, also referred to as its ‘length’.
//
// heap := gost.BinaryHeapNew[gost.I32]()
// gost.AssertEq(heap.Len(), gost.USize(0))
//
// heap.Push(1)
// gost.AssertEq(heap.Len(), gost.USize(1))
func (self BinaryHeap[T]) Len() USize {
return self.vec.Len()
}
// Returns true if the heap contains no elements.
//
// heap := gost.BinaryHeapNew[gost.I32]()
// gost.Assert(heap.IsEmpty())
//
// heap.Push(1)
// gost.Assert(!heap.IsEmpty())
func (self BinaryHeap[T]) IsEmpty() Bool {
return self.vec.IsEmpty()
}
// Reserves capacity for at least additional more elements to be inserted in the given Vec<T>. The collection may reserve more space to speculatively avoid frequent reallocations. After calling reserve, capacity will be greater than or equal to self.len() + additional. Does nothing if capacity is already sufficient.
//
// deque := gost.BinaryHeapNew[gost.I32]()
// gost.AssertEq(deque.Capacity(), gost.USize(0))
//
// deque.Reserve(10)
// gost.AssertEq(deque.Capacity(), gost.USize(10))
func (self *BinaryHeap[T]) Reserve(capacity USize) {
self.vec.Reserve(capacity)
}
// Pushes an item onto the binary heap.
//
// heap := gost.BinaryHeapNew[gost.I32]()
// heap.Push(1)
// gost.AssertEq(heap.Len(), gost.USize(1))
func (self *BinaryHeap[T]) Push(item T) {
oldLen := self.Len()
self.vec.Push(item)
// SAFETY: Since we pushed a new item it means that
// old_len = self.len() - 1 < self.len()
self._SiftUp(0, oldLen)
}
func (self *BinaryHeap[T]) _SiftUp(start USize, pos USize) USize {
// Take out the value at `pos` and create a hole.
// SAFETY: The caller guarantees that pos < self.len()
hole := _HoleNew[T](&self.vec, pos)
for hole.Pos() > start {
parent := (hole.Pos() - 1) / 2
// SAFETY: hole.pos() > start >= 0, which means hole.pos() > 0
// and so hole.pos() - 1 can't underflow.
// This guarantees that parent < hole.pos() so
// it's a valid index and also != hole.pos().
order := hole.Element().Cmp(hole.Get(parent))
if order == OrderingLess || order == OrderingEqual {
break
}
// SAFETY: Same as above
hole.MoveTo(parent)
}
return hole.Pos()
}
// Removes the greatest item from the binary heap and returns it, or None if it is empty.
//
// heap := gost.BinaryHeapNew[gost.I32]()
// heap.Push(1)
// heap.Push(2)
// heap.Push(3)
// gost.AssertEq(heap.Len(), gost.I32(3))
// gost.AssertEq(heap.Pop(), gost.Some[gost.I32](3))
// gost.AssertEq(heap.Len(), gost.I32(2))
func (self *BinaryHeap[T]) Pop() Option[T] {
return self.vec.Pop().Map(func(item T) T {
if !self.IsEmpty() {
_Swap(&item, &self.vec.data[0])
// SAFETY: !self.is_empty() means that self.len() > 0
self._SiftDownToBottom(0)
}
return item
})
}
func (self *BinaryHeap[T]) _SiftDownToBottom(pos USize) {
end := self.Len()
start := pos
// SAFETY: pos < self.len() so pos + 1 <= self.len()
hole := _HoleNew[T](&self.vec, pos)
child := 2*hole.Pos() + 1
// Loop invariant: child == 2 * hole.pos() + 1.
for child <= end.SaturatingSub(2) {
// SAFETY: child < end - 1 < self.len() and
// child + 1 < end <= self.len(), so they're valid indexes.
// child == 2 * hole.pos() + 1 != hole.pos() and
// child + 1 == 2 * hole.pos() + 2 != hole.pos().
// FIXME: 2 * hole.pos() + 1 or 2 * hole.pos() + 2 could overflow
// if T is a ZST
ordering := hole.Get(child).Cmp(hole.Get(child + 1))
if ordering == OrderingLess || ordering == OrderingEqual {
child += 1
}
// SAFETY: Same as above
hole.MoveTo(child)
child = 2*hole.Pos() + 1
}
if child == end-1 {
// SAFETY: child == end - 1 < self.len(), so it's a valid index
// and child == 2 * hole.pos() + 1 != hole.pos().
hole.MoveTo(child)
}
pos = hole.Pos()
// SAFETY: pos is the position in the hole and was already proven
// to be a valid index.
self._SiftUp(start, pos)
}
// Moves all the elements of other into self, leaving other empty.
//
// heap := gost.BinaryHeapNew[gost.I32]()
// heap.Push(1)
// heap.Push(2)
// heap.Push(3)
// heap2 := gost.BinaryHeapNew[gost.I32]()
// heap2.Push(4)
// heap2.Push(5)
// heap.Append(&heap2)
// gost.AssertEq(heap.Len(), gost.USize(5))
// gost.AssertEq(heap2.Len(), gost.USize(0))
func (self *BinaryHeap[T]) Append(other *BinaryHeap[T]) {
self.Reserve(self.Len() + other.Len())
for _, item := range other.vec.data {
self.Push(item)
}
}
// Clears the heap, removing all values.
//
// heap := gost.BinaryHeapNew[gost.I32]()
// heap.Push(1)
// heap.Push(2)
// heap.Push(3)
// heap.Clear()
// gost.AssertEq(heap.Len(), gost.USize(0))
func (self *BinaryHeap[T]) Clear() {
self.vec.Clear()
}
// Extracts a slice containing the entire heap.
func (self *BinaryHeap[T]) AsSlice() []T {
return self.vec.AsSlice()
}
// Consumes the BinaryHeap and returns the underlying vector in arbitrary order.
//
// heap := gost.BinaryHeapNew[gost.I32]()
// heap.Push(1)
// heap.Push(2)
// heap.Push(3)
// vec := gost.VecNew[gost.I32]()
// vec.Push(3)
// vec.Push(1)
// vec.Push(2)
// gost.AssertEq(heap.IntoVec(), vec)
func (self *BinaryHeap[T]) IntoVec() Vec[T] {
return self.vec
}
// Consumes the BinaryHeap and returns a vector in sorted (ascending) order.
//
// heap := gost.BinaryHeapNew[gost.I32]()
// heap.Push(1)
// heap.Push(3)
// heap.Push(2)
// vec := gost.VecNew[gost.I32]()
// vec.Push(1)
// vec.Push(2)
// vec.Push(3)
// gost.AssertEq(heap.IntoSortedVec(), vec)
func (self *BinaryHeap[T]) IntoSortedVec() Vec[T] {
sortedVec := VecWithLen[T](self.Len())
index := self.Len() - 1
for !self.IsEmpty() {
sortedVec.SetUnchecked(index, self.Pop().Unwrap())
index -= 1
}
return sortedVec
}
/// Hole represents a hole in a slice i.e., an index without valid value
/// (because it was moved from or duplicated).
/// In drop, `Hole` will restore the slice by filling the hole
/// position with the value that was originally removed.
type _Hole[T any] struct {
data *Vec[T]
elt T
pos USize
}
func _HoleNew[T any](data *Vec[T], pos USize) _Hole[T] {
elt := data.GetUnchecked(pos)
return _Hole[T]{
data: data,
elt: elt,
pos: pos,
}
}
func (self _Hole[T]) Pos() USize {
return self.pos
}
// Returns a reference to the element removed.
func (self _Hole[T]) Element() T {
return self.elt
}
/// Returns a reference to the element at `index`.
/// Unsafe because index must be within the data slice and not equal to pos.
func (self _Hole[T]) Get(pos USize) T {
return self.data.data[pos]
}
// Move hole to new location
// Unsafe because index must be within the data slice and not equal to pos.
func (self *_Hole[T]) MoveTo(index USize) {
self.data.Swap(self.pos, index)
self.pos = index
}