forked from mirror/tinyqueue
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