You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
4.1 KiB
Go
156 lines
4.1 KiB
Go
package queue
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"git.wens.org.uk/heapqueue/pkg/heap"
|
|
"golang.org/x/exp/constraints"
|
|
)
|
|
|
|
//QueueType is an enum for min/max queue
|
|
type QueueType bool
|
|
|
|
const (
|
|
MINQUEUE QueueType = false
|
|
MAXQUEUE QueueType = true
|
|
)
|
|
|
|
// Queue data type, holding a slice of generic queue objects
|
|
type Queue[Orderable constraints.Ordered, T any] struct {
|
|
store []QueueObject[Orderable, T]
|
|
queueSize int
|
|
}
|
|
|
|
// QueueObject holds generic types for key (comparable with > and < only), and any for value
|
|
type QueueObject[Orderable constraints.Ordered, T any] struct {
|
|
Key Orderable
|
|
Value T
|
|
}
|
|
|
|
// BuildQueue takes an input slice of queue objects and the type of queue, returning a pointer to the constructed priority queue
|
|
func BuildQueue[Orderable constraints.Ordered, T any](d []QueueObject[Orderable, T], qType QueueType) *Queue[Orderable, T] {
|
|
q := &Queue[Orderable, T]{d, len(d)}
|
|
for i := len(d) / 2; i >= 0; i-- {
|
|
if qType == MAXQUEUE {
|
|
maxHeapify(q, i)
|
|
} else {
|
|
minHeapify(q, i)
|
|
}
|
|
}
|
|
return q
|
|
}
|
|
|
|
// exchange two items within the context of the queue. requires max/min heapify afterwards
|
|
func exchange[Orderable constraints.Ordered, T any](q *Queue[Orderable, T], x, y int) {
|
|
q.store[x], q.store[y] = q.store[y], q.store[x]
|
|
}
|
|
|
|
// maxHeapify reorders into a max heap starting at the input index i
|
|
func maxHeapify[Orderable constraints.Ordered, T any](q *Queue[Orderable, T], i int) {
|
|
l, r := heap.Left(i), heap.Right(i)
|
|
|
|
var largest int
|
|
if l < q.queueSize && q.store[l].Key > q.store[i].Key {
|
|
largest = l
|
|
} else {
|
|
largest = i
|
|
}
|
|
if r < q.queueSize && q.store[r].Key > q.store[largest].Key {
|
|
largest = r
|
|
}
|
|
|
|
if largest != i {
|
|
exchange(q, i, largest)
|
|
maxHeapify(q, largest)
|
|
}
|
|
}
|
|
|
|
// minHeapify reorders into a min heap starting at the input index i
|
|
func minHeapify[Orderable constraints.Ordered, T any](q *Queue[Orderable, T], i int) {
|
|
l, r := heap.Left(i), heap.Right(i)
|
|
|
|
var smallest int
|
|
if l < q.queueSize && q.store[l].Key < q.store[i].Key {
|
|
smallest = l
|
|
} else {
|
|
smallest = i
|
|
}
|
|
if r < q.queueSize && q.store[r].Key < q.store[smallest].Key {
|
|
smallest = r
|
|
}
|
|
|
|
if smallest != i {
|
|
exchange(q, i, smallest)
|
|
minHeapify(q, smallest)
|
|
}
|
|
}
|
|
|
|
// Maximum returns the QueueObject with the highest priority
|
|
func Maximum[Orderable constraints.Ordered, T any](q *Queue[Orderable, T]) (ret QueueObject[Orderable, T], e error) {
|
|
if q.queueSize < 1 {
|
|
e = fmt.Errorf("heap underflow")
|
|
return
|
|
}
|
|
|
|
ret = q.store[0]
|
|
return
|
|
}
|
|
|
|
// ExtractMaximum returns the QueueObject with the highest priority and removes it from the queue, reordering afterwards
|
|
func ExtractMaximum[Orderable constraints.Ordered, T any](q *Queue[Orderable, T]) (ret QueueObject[Orderable, T], e error) {
|
|
ret, e = Maximum(q)
|
|
if e != nil {
|
|
return
|
|
}
|
|
|
|
q.store[0] = q.store[q.queueSize-1]
|
|
q.queueSize--
|
|
q.store = q.store[0:q.queueSize]
|
|
|
|
maxHeapify(q, 0)
|
|
|
|
return
|
|
}
|
|
|
|
// IncraseKey raises the priority of a given queue item and reorders the queue
|
|
func IncreaseKey[Orderable constraints.Ordered, T any](q *Queue[Orderable, T], item QueueObject[Orderable, T], newKey Orderable) error {
|
|
if newKey < item.Key {
|
|
return fmt.Errorf("new key %v is smaller than current key %v", newKey, item.Key)
|
|
}
|
|
|
|
var i int
|
|
for idx, qObj := range q.store {
|
|
if reflect.DeepEqual(qObj, item) {
|
|
q.store[idx] = QueueObject[Orderable, T]{Key: newKey, Value: qObj.Value}
|
|
i = idx
|
|
}
|
|
}
|
|
|
|
for q.store[heap.Parent(i)].Key < q.store[i].Key {
|
|
exchange(q, i, heap.Parent(i))
|
|
i = heap.Parent(i)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Insert adds a new item to the queue
|
|
func Insert[Orderable constraints.Ordered, T any](q *Queue[Orderable, T], item QueueObject[Orderable, T]) {
|
|
q.queueSize++
|
|
k := item.Key
|
|
insert := QueueObject[Orderable, T]{Value: item.Value}
|
|
q.store = append(q.store, insert)
|
|
IncreaseKey(q, insert, k)
|
|
}
|
|
|
|
// IsMaxQueue returns true when the queue satisfies the max queue property i.e. holds a valid structure for a max queue
|
|
func IsMaxQueue[Orderable constraints.Ordered, T any](q *Queue[Orderable, T]) bool {
|
|
for index, val := range q.store {
|
|
if q.store[heap.Parent(index)].Key < val.Key {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|