mirror of https://github.com/tidwall/tinyqueue.git
first commit
This commit is contained in:
commit
fc4cef0a81
|
@ -0,0 +1,15 @@
|
|||
ISC License
|
||||
|
||||
Copyright (c) 2017, Vladimir Agafonkin
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
with or without fee is hereby granted, provided that the above copyright notice
|
||||
and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
|
@ -0,0 +1,7 @@
|
|||
# tinyqueue
|
||||
<a href="https://godoc.org/github.com/tidwall/tinyqueue"><img src="https://img.shields.io/badge/api-reference-blue.svg?style=flat-square" alt="GoDoc"></a>
|
||||
|
||||
tinyqueue is a Go package for binary heap priority queues.
|
||||
Ported from the [tinyqueue](https://github.com/mourner/tinyqueue) Javascript library.
|
||||
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package tinyqueue
|
||||
|
||||
type Queue struct {
|
||||
length int
|
||||
data []Item
|
||||
}
|
||||
|
||||
type Item interface {
|
||||
Less(Item) bool
|
||||
}
|
||||
|
||||
func New(data []Item) *Queue {
|
||||
q := &Queue{}
|
||||
q.data = data
|
||||
q.length = len(data)
|
||||
if q.length > 0 {
|
||||
i := q.length >> 1
|
||||
for ; i >= 0; i-- {
|
||||
q.down(i)
|
||||
}
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func (q *Queue) Push(item Item) {
|
||||
q.data = append(q.data, item)
|
||||
q.length++
|
||||
q.up(q.length - 1)
|
||||
}
|
||||
func (q *Queue) Pop() Item {
|
||||
if q.length == 0 {
|
||||
return nil
|
||||
}
|
||||
top := q.data[0]
|
||||
q.length--
|
||||
if q.length > 0 {
|
||||
q.data[0] = q.data[q.length]
|
||||
q.down(0)
|
||||
}
|
||||
q.data = q.data[:len(q.data)-1]
|
||||
return top
|
||||
}
|
||||
func (q *Queue) Peek() Item {
|
||||
if q.length == 0 {
|
||||
return nil
|
||||
}
|
||||
return q.data[0]
|
||||
}
|
||||
|
||||
func (q *Queue) down(pos int) {
|
||||
data := q.data
|
||||
halfLength := q.length >> 1
|
||||
item := data[pos]
|
||||
for pos < halfLength {
|
||||
left := (pos << 1) + 1
|
||||
right := left + 1
|
||||
best := data[left]
|
||||
if right < q.length && data[right].Less(best) {
|
||||
left = right
|
||||
best = data[right]
|
||||
}
|
||||
if !best.Less(item) {
|
||||
break
|
||||
}
|
||||
data[pos] = best
|
||||
pos = left
|
||||
}
|
||||
data[pos] = item
|
||||
}
|
||||
|
||||
func (q *Queue) up(pos int) {
|
||||
data := q.data
|
||||
item := data[pos]
|
||||
for pos > 0 {
|
||||
parent := (pos - 1) >> 1
|
||||
current := data[parent]
|
||||
if !item.Less(current) {
|
||||
break
|
||||
}
|
||||
data[pos] = current
|
||||
pos = parent
|
||||
}
|
||||
data[pos] = item
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package tinyqueue
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/json-iterator/go/assert"
|
||||
)
|
||||
|
||||
type floatValue float64
|
||||
|
||||
func (a floatValue) Less(b Item) bool {
|
||||
return a < b.(floatValue)
|
||||
}
|
||||
|
||||
var data, sorted = func() ([]Item, []Item) {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
var data []Item
|
||||
for i := 0; i < 100; i++ {
|
||||
data = append(data, floatValue(rand.Float64()*100))
|
||||
}
|
||||
sorted := make([]Item, len(data))
|
||||
copy(sorted, data)
|
||||
sort.Slice(sorted, func(i, j int) bool {
|
||||
return sorted[i].Less(sorted[j])
|
||||
})
|
||||
return data, sorted
|
||||
}()
|
||||
|
||||
func TestMaintainsPriorityQueue(t *testing.T) {
|
||||
q := New(nil)
|
||||
for i := 0; i < len(data); i++ {
|
||||
q.Push(data[i])
|
||||
}
|
||||
assert.Equal(t, q.Peek(), sorted[0])
|
||||
var result []Item
|
||||
for q.length > 0 {
|
||||
result = append(result, q.Pop())
|
||||
}
|
||||
assert.Equal(t, result, sorted)
|
||||
}
|
||||
|
||||
func TestAcceptsDataInConstructor(t *testing.T) {
|
||||
q := New(data)
|
||||
var result []Item
|
||||
for q.length > 0 {
|
||||
result = append(result, q.Pop())
|
||||
}
|
||||
assert.Equal(t, result, sorted)
|
||||
}
|
||||
func TestHandlesEdgeCasesWithFewElements(t *testing.T) {
|
||||
q := New(nil)
|
||||
q.Push(floatValue(2))
|
||||
q.Push(floatValue(1))
|
||||
q.Pop()
|
||||
q.Pop()
|
||||
q.Pop()
|
||||
q.Push(floatValue(2))
|
||||
q.Push(floatValue(1))
|
||||
assert.Equal(t, float64(q.Pop().(floatValue)), 1.0)
|
||||
assert.Equal(t, float64(q.Pop().(floatValue)), 2.0)
|
||||
assert.Equal(t, q.Pop(), nil)
|
||||
}
|
Loading…
Reference in New Issue