1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2025-02-20 23:47:21 -05:00
v2fly/common/pq/priorityqueue.go
2023-07-25 16:16:38 +08:00

183 lines
4.5 KiB
Go

// 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,
}
}