|
|
@@ -0,0 +1,182 @@
|
|
|
+// modified from github.com/oleiade/lane
|
|
|
+
|
|
|
+package pq
|
|
|
+
|
|
|
+import (
|
|
|
+ "golang.org/x/exp/constraints"
|
|
|
+)
|
|
|
+
|
|
|
+// PriorityQueue is a heap-based priority-queue data structure implementation.
|
|
|
+//
|
|
|
+// It can either be min (ascending) or max (descending)
|
|
|
+// oriented/ordered. Its type parameters `T` and `P`, respectively
|
|
|
+// specify the underlying value type and the underlying priority type.
|
|
|
+type PriorityQueue[T any, P constraints.Ordered] struct {
|
|
|
+ items []*priorityQueueItem[T, P]
|
|
|
+ itemCount uint
|
|
|
+ comparator func(lhs, rhs P) bool
|
|
|
+}
|
|
|
+
|
|
|
+// NewPriorityQueue instantiates a new PriorityQueue with the provided comparison heuristic.
|
|
|
+// The package defines the `Max` and `Min` heuristic to define a max-oriented or
|
|
|
+// min-oriented heuristics, respectively.
|
|
|
+func NewPriorityQueue[T any, P constraints.Ordered](heuristic func(lhs, rhs P) bool) *PriorityQueue[T, P] {
|
|
|
+ items := make([]*priorityQueueItem[T, P], 1)
|
|
|
+ items[0] = nil
|
|
|
+
|
|
|
+ return &PriorityQueue[T, P]{
|
|
|
+ items: items,
|
|
|
+ itemCount: 0,
|
|
|
+ comparator: heuristic,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// NewMaxPriorityQueue instantiates a new maximum oriented PriorityQueue.
|
|
|
+func NewMaxPriorityQueue[T any, P constraints.Ordered]() *PriorityQueue[T, P] {
|
|
|
+ return NewPriorityQueue[T](Maximum[P])
|
|
|
+}
|
|
|
+
|
|
|
+// NewMinPriorityQueue instantiates a new minimum oriented PriorityQueue.
|
|
|
+func NewMinPriorityQueue[T any, P constraints.Ordered]() *PriorityQueue[T, P] {
|
|
|
+ return NewPriorityQueue[T](Minimum[P])
|
|
|
+}
|
|
|
+
|
|
|
+// Maximum returns whether `rhs` is greater than `lhs`.
|
|
|
+//
|
|
|
+// Use it as a comparison heuristic during a PriorityQueue's
|
|
|
+// instantiation.
|
|
|
+func Maximum[T constraints.Ordered](lhs, rhs T) bool {
|
|
|
+ return lhs < rhs
|
|
|
+}
|
|
|
+
|
|
|
+// Minimum returns whether `rhs` is less than `lhs`.
|
|
|
+//
|
|
|
+// Use it as a comparison heuristic during a PriorityQueue's
|
|
|
+// instantiation.
|
|
|
+func Minimum[T constraints.Ordered](lhs, rhs T) bool {
|
|
|
+ return lhs > rhs
|
|
|
+}
|
|
|
+
|
|
|
+// Push inserts the value in the PriorityQueue with the provided priority
|
|
|
+// in at most *O(log n)* time complexity.
|
|
|
+func (pq *PriorityQueue[T, P]) Push(value T, priority P) {
|
|
|
+ item := newPriorityQueueItem(value, priority)
|
|
|
+
|
|
|
+ pq.items = append(pq.items, item)
|
|
|
+ pq.itemCount++
|
|
|
+ pq.swim(pq.size())
|
|
|
+}
|
|
|
+
|
|
|
+// Pop and return the highest or lowest priority item (depending on the
|
|
|
+// comparison heuristic of your PriorityQueue) from the PriorityQueue in
|
|
|
+// at most *O(log n)* complexity.
|
|
|
+func (pq *PriorityQueue[T, P]) Pop() (value T, priority P, ok bool) {
|
|
|
+
|
|
|
+ if pq.size() < 1 {
|
|
|
+ ok = false
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ max := pq.items[1]
|
|
|
+ pq.exch(1, pq.size())
|
|
|
+ pq.items = pq.items[0:pq.size()]
|
|
|
+ pq.itemCount--
|
|
|
+ pq.sink(1)
|
|
|
+
|
|
|
+ value = max.value
|
|
|
+ priority = max.priority
|
|
|
+ ok = true
|
|
|
+
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// Head returns the highest or lowest priority item (depending on
|
|
|
+// the comparison heuristic of your PriorityQueue) from the PriorityQueue
|
|
|
+// in *O(1)* complexity.
|
|
|
+func (pq *PriorityQueue[T, P]) Head() (value T, priority P, ok bool) {
|
|
|
+
|
|
|
+ if pq.size() < 1 {
|
|
|
+ ok = false
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ value = pq.items[1].value
|
|
|
+ priority = pq.items[1].priority
|
|
|
+ ok = true
|
|
|
+
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// Size returns the number of elements present in the PriorityQueue.
|
|
|
+func (pq *PriorityQueue[T, P]) Size() uint {
|
|
|
+
|
|
|
+ return pq.size()
|
|
|
+}
|
|
|
+
|
|
|
+// Empty returns whether the PriorityQueue is empty.
|
|
|
+func (pq *PriorityQueue[T, P]) Empty() bool {
|
|
|
+
|
|
|
+ return pq.size() == 0
|
|
|
+}
|
|
|
+
|
|
|
+// Clone returns a copy of the PriorityQueue.
|
|
|
+func (pq *PriorityQueue[T, P]) Clone() *PriorityQueue[T, P] {
|
|
|
+ items := make([]*priorityQueueItem[T, P], len(pq.items))
|
|
|
+ copy(items, pq.items)
|
|
|
+
|
|
|
+ return &PriorityQueue[T, P]{
|
|
|
+ items: items,
|
|
|
+ itemCount: pq.itemCount,
|
|
|
+ comparator: pq.comparator,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (pq *PriorityQueue[T, P]) swim(k uint) {
|
|
|
+ for k > 1 && pq.less(k/2, k) {
|
|
|
+ pq.exch(k/2, k)
|
|
|
+ k /= 2
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (pq *PriorityQueue[T, P]) sink(k uint) {
|
|
|
+ for 2*k <= pq.size() {
|
|
|
+ j := 2 * k
|
|
|
+
|
|
|
+ if j < pq.size() && pq.less(j, j+1) {
|
|
|
+ j++
|
|
|
+ }
|
|
|
+
|
|
|
+ if !pq.less(k, j) {
|
|
|
+ break
|
|
|
+ }
|
|
|
+
|
|
|
+ pq.exch(k, j)
|
|
|
+ k = j
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (pq *PriorityQueue[T, P]) size() uint {
|
|
|
+ return pq.itemCount
|
|
|
+}
|
|
|
+
|
|
|
+func (pq *PriorityQueue[T, P]) less(lhs, rhs uint) bool {
|
|
|
+ return pq.comparator(pq.items[lhs].priority, pq.items[rhs].priority)
|
|
|
+}
|
|
|
+
|
|
|
+func (pq *PriorityQueue[T, P]) exch(lhs, rhs uint) {
|
|
|
+ pq.items[lhs], pq.items[rhs] = pq.items[rhs], pq.items[lhs]
|
|
|
+}
|
|
|
+
|
|
|
+// priorityQueueItem is the underlying PriorityQueue item container.
|
|
|
+type priorityQueueItem[T any, P constraints.Ordered] struct {
|
|
|
+ value T
|
|
|
+ priority P
|
|
|
+}
|
|
|
+
|
|
|
+// newPriorityQueue instantiates a new priorityQueueItem.
|
|
|
+func newPriorityQueueItem[T any, P constraints.Ordered](value T, priority P) *priorityQueueItem[T, P] {
|
|
|
+ return &priorityQueueItem[T, P]{
|
|
|
+ value: value,
|
|
|
+ priority: priority,
|
|
|
+ }
|
|
|
+}
|