algorithms/sorting/priority_queue.go

114 lines
2.6 KiB
Go
Raw Normal View History

2021-12-31 13:26:22 +02:00
package sorting
type PriorityQueue[T any] interface {
top() T
insert(T)
delete() T
size() int
isEmpty() bool
}
type priorityQueue[T any] struct {
n int
heap []T
2022-01-01 20:50:20 +02:00
// Actually this is "less" function.
// First element not necessary is less than second,
// Can be used both ways to get reversed priority
2021-12-31 13:26:22 +02:00
less func(T, T) bool
}
func NewPQ[T any](less func(T, T) bool) PriorityQueue[T] {
return &priorityQueue[T]{
n: 0,
less: less,
// First element is not used
// to make equation "next = current * 2" work
heap: make([]T, 1, 1),
}
}
func (pq *priorityQueue[T]) top() T {
2022-01-01 20:50:20 +02:00
// After all operations "biggest" element
2021-12-31 13:26:22 +02:00
// should be always at the beginning of the array
return pq.heap[1]
}
func (pq *priorityQueue[T]) insert(item T) {
2022-01-01 21:24:19 +02:00
// We can ignore "resize optimization":
// append function will create new array for new slice
// with doubled capacity when needed
// https://github.com/golang/go/blob/master/src/runtime/slice.go#L188
// https://go.dev/blog/slices-intro
// https://go.dev/play/p/OKtCFskbp2t
// https://stackoverflow.com/questions/23531737/how-the-slice-is-enlarged-by-append-is-the-capacity-always-doubled
2021-12-31 13:26:22 +02:00
pq.heap = append(pq.heap, item)
pq.n++
pq.swim(pq.n)
}
func (pq *priorityQueue[T]) delete() T {
top := pq.top()
pq.swap(1, pq.n)
2022-01-01 20:50:20 +02:00
// Discard "biggest" element from queue
// for possible GC
2021-12-31 13:26:22 +02:00
pq.heap = pq.heap[:pq.n]
pq.n--
2022-01-01 20:50:20 +02:00
// Reshape heap from top to bottom
2021-12-31 13:26:22 +02:00
pq.sink(1)
return top
}
func (pq *priorityQueue[_]) size() int {
return pq.n
}
func (pq *priorityQueue[_]) isEmpty() bool {
return pq.n == 0
}
func (pq *priorityQueue[_]) swap(i, j int) {
pq.heap[i], pq.heap[j] = pq.heap[j], pq.heap[i]
}
2022-01-01 20:50:20 +02:00
func (pq *priorityQueue[T]) swim(child int) {
// Until we reach top of the heap
// and parent node is less than current child
for child > 1 && pq.less(pq.heap[child/2], pq.heap[child]) {
// We swap parent with the child
pq.swap(child/2, child)
// Parent node becomes new child
// for next iteration
child = child / 2
2021-12-31 13:26:22 +02:00
}
}
2022-01-01 20:50:20 +02:00
func (pq *priorityQueue[T]) sink(parent int) {
// While parent has some children
for 2*parent <= pq.n {
2021-12-31 13:26:22 +02:00
2022-01-01 20:50:20 +02:00
// First child of a parent
child := 2 * parent
2021-12-31 13:26:22 +02:00
// If first child is less than second
// we choose second one for exchange.
// Parent should be swapped with biggest child
2022-01-01 20:50:20 +02:00
if child < pq.n && pq.less(pq.heap[child], pq.heap[child+1]) {
child++
2021-12-31 13:26:22 +02:00
}
// If parent is already bigger than biggest child
// we found the position
2022-01-01 20:50:20 +02:00
if !pq.less(pq.heap[parent], pq.heap[child]) {
2021-12-31 13:26:22 +02:00
break
}
// swap parent node with child
2022-01-01 20:50:20 +02:00
pq.swap(parent, child)
2021-12-31 13:26:22 +02:00
// child node is a new parent
2022-01-01 20:50:20 +02:00
parent = child
2021-12-31 13:26:22 +02:00
}
}