Giter Site home page Giter Site logo

oleiade / lane Goto Github PK

View Code? Open in Web Editor NEW
871.0 19.0 76.0 120 KB

Generic PriorityQueues, Queues, Stacks, and Deque data structures for Go

Home Page: https://pkg.go.dev/github.com/oleiade/lane#pkg-types

License: MIT License

Go 100.00%
data-structures deque generic go priority-queue queue stack

lane's Introduction

Lane

MIT License Build Status Go Documentation Go Report Card Go Version

The Lane package provides textbook implementations of generic Queue, PriorityQueue, Stack, and Deque data structures. Its design focuses on simplicity, performance, and concurrent usage.

Installation

Using this package requires a working Go environment. See the install instructions for Go.

This package requires a modern version of Go supporting modules: see the go blog guide on using Go Modules.

Using v2 releases

go get github.com/oleiade/lane/v2
...
import (
 "github.com/oleiade/lane/v2"
)
...

Using v1 releases

go get github.com/oleiade/lane
...
import (
 "github.com/oleiade/lane"
)
...

Usage/Examples

Priority queue

PriorityQueue implements a heap priority queue data structure. It can be either max (descending) or min (ascending) ordered. Every operation on a PriorityQueue is goroutine-safe. It performs Push and Pop operations in O(log N) time.

Example

// Create a new max ordered priority queue
priorityQueue := NewMaxPriorityQueue[string, int]()

// And push some prioritized content into it
priorityQueue.Push("easy as", 3)
priorityQueue.Push("123", 2)
priorityQueue.Push("do re mi", 4)
priorityQueue.Push("abc", 1)

// Take a look at the min element in
// the priority queue
headValue, headPriority, ok := priorityQueue.Head()
if ok {
    fmt.Println(headValue)    // "abc"
    fmt.Println(headPriority) // 1
}

// The operations seem to preserve the song order
jacksonFive := make([]string, priorityQueue.Size())

for i := 0; i < len(jacksonFive); i++ {
    value, _, _ := priorityQueue.Pop()
    jacksonFive[i] = value
}

fmt.Println(strings.Join(jacksonFive, " "))

Deque

Deque implements a head-tail linked list data structure. Built upon a doubly linked list container, every operation performed on a Deque happen in O(1) time complexity. Every operation on a Deque are goroutine-safe.

Users have the option to instantiate Deques with a limited capacity using the dedicated NewBoundDeque constructor. When a bound Deque is full, the Append and Prepend operations fail.

Deque example

// Create a new Deque data structure
deque := NewDeque[string]()

// And push some content into it using the Append
// and Prepend methods
deque.Append("easy as")
deque.Prepend("123")
deque.Append("do re mi")
deque.Prepend("abc")

// Take a look at what the first and
// last element stored in the Deque are.
firstValue, ok := deque.First()
if ok {
    fmt.Println(firstValue) // "abc"
}

lastValue, ok := deque.Last()
if ok {
    fmt.Println(lastValue) // 1
}

// Use the `Pop` and `Shift`
// methods to bring the song words together
jacksonFive := make([]string, deque.Size())

for i := 0; i < len(jacksonFive); i++ {
    value, ok := deque.Shift()
    if ok {
        jacksonFive[i] = value
    }
}

// abc 123 easy as do re mi
fmt.Println(strings.Join(jacksonFive, " "))

Queue

Queue is a FIFO (First In First Out) data structure implementation. Built upon a Deque container, it focuses its API on the following core functionalities: Enqueue, Dequeue, Head. Every operation on a Queue has a time complexity of O(1). Every operation on a Queue is goroutine-safe.

Queue example

// Create a new queue and pretend to handle Starbucks clients
queue := NewQueue[string]()

// Add the incoming clients to the queue
queue.Enqueue("grumpyClient")
queue.Enqueue("happyClient")
queue.Enqueue("ecstaticClient")

fmt.Println(queue.Head()) // grumpyClient

// Handle the clients asynchronously
for {
    client, ok := queue.Dequeue()
    if !ok {
        break
    }

    fmt.Println(client)
}

Stack

Stack implements a Last In First Out data structure. Built upon a Deque container, its API focuses on the following core functionalities: Push, Pop, Head. Every operation on a Stack has a time complexity of O(1). Every operation on a Stack is goroutine-safe.

Stack example

// Create a new stack and put some plates over it
stack := NewStack[string]()

// Put some plates on the stack
stack.Push("redPlate")
stack.Push("bluePlate")
stack.Push("greenPlate")

fmt.Println(stack.Head()) // greenPlate

// Check the top of the stack
value, ok := stack.Pop()
if ok {
    fmt.Println(value) // greenPlate
}

stack.Push("yellowPlate")

value, ok = stack.Pop()
if ok {
    fmt.Println(value) // yellowPlate
}

// Check the top of the stack
value, ok = stack.Pop()
if ok {
    fmt.Println(value) // bluePlate
}

// Check the top of the stack
value, ok = stack.Pop()
if ok {
    fmt.Println(value) // redPlate
}

Performance

go test -bench=.
goos: darwin
goarch: arm64
pkg: github.com/oleiade/lane/v2
BenchmarkDequeAppend-8          22954384        54.44 ns/op      32 B/op       1 allocs/op
BenchmarkDequePrepend-8         25097098        44.59 ns/op      32 B/op       1 allocs/op
BenchmarkDequePop-8             63403720        18.99 ns/op       0 B/op       0 allocs/op
BenchmarkDequeShift-8           63390186        20.88 ns/op       0 B/op       0 allocs/op
BenchmarkDequeFirst-8           86662152        13.76 ns/op       0 B/op       0 allocs/op
BenchmarkDequeLast-8            85955014        13.76 ns/op       0 B/op       0 allocs/op
BenchmarkDequeSize-8            86614975        13.77 ns/op       0 B/op       0 allocs/op
BenchmarkDequeEmpty-8           86893297        14.22 ns/op       0 B/op       0 allocs/op
BenchmarkBoundDequeFull-8       590152324         2.039 ns/op       0 B/op       0 allocs/op
BenchmarkBoundDequeAppend-8     64457216        18.50 ns/op       0 B/op       0 allocs/op
BenchmarkBoundDeque-8           64631377        18.48 ns/op       0 B/op       0 allocs/op
BenchmarkPriorityQueuePush-8    19994029        65.67 ns/op      72 B/op       1 allocs/op
BenchmarkPriorityQueuePop-8     62751249        18.52 ns/op       0 B/op       0 allocs/op
BenchmarkPriorityQueueHead-8    86090420        13.77 ns/op       0 B/op       0 allocs/op
BenchmarkPriorityQueueSize-8    86768415        13.77 ns/op       0 B/op       0 allocs/op
BenchmarkPriorityQueueEmpty-8   87036146        13.76 ns/op       0 B/op       0 allocs/op
BenchmarkNewQueue-8             19570003        60.36 ns/op
BenchmarkQueueEnqueue-8         25482283        46.63 ns/op      32 B/op       1 allocs/op
BenchmarkQueueDequeue-8         63715965        18.50 ns/op       0 B/op       0 allocs/op
BenchmarkQueueHead-8            85664312        13.79 ns/op       0 B/op       0 allocs/op
BenchmarkNewStack-8             19925473        59.57 ns/op
BenchmarkStackPop-8             64704993        18.80 ns/op       0 B/op       0 allocs/op
BenchmarkStackHead-8            86119761        13.76 ns/op       0 B/op       0 allocs/op

Documentation

For a more detailed overview of lane, please refer to Documentation

License

MIT

lane's People

Contributors

anantn avatar bitdeli-chef avatar ericyt avatar oleiade avatar rcaraveo avatar scr4t avatar tcr-ableton avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lane's Issues

Pop but no Push in Queue?

Hi,

I find it might be a little confusing that Queue has Pop() method but does not has Push(). I know normally Queue should not be used as Stack but it's weird that it should has neither or both of Pop() and Push().

Unit Test Breaks with 4 elements in queue

I was using your repository for a Median Maintenance algorithm using two Priority Queues: One supports Max, one supports Min. Your library is perfect, it seemed, for this.

But here is what happens in a simple case:

hLow := NewPQueue(lane.MAXPQ)
hLow.Push("1",1)
hLow.Push("2",2)
hLow.Push("3",3)
hLow.Push("4",4) // Pop order at this point: 3/3 4/4 2/2 1/1
v,p := hLow.Pop() // should be 4/4 -> get 3/3
hLow.Push("4",4) // resulting Pop order: 4/4 4/4 2/2 1/1

and I can break your Unit Test just by adding a 4th element in the queue:

func TestMaxPQueuePush_protects_max_order(t *testing.T) {
pqueue := NewPQueue(MAXPQ)

pqueue.Push("1", 1)
pqueue.Push("2", 2)
pqueue.Push("3", 3)
pqueue.Push("4", 4)

// Heap queue index starts at one, zero index should always be nil
firstItem := pqueue.items[1]
firstItemValue, ok := firstItem.value.(string)
assert.True(t, ok)
assert.Equal(t, firstItemValue, "4")    // pqueue_test.go:38
assert.Equal(t, firstItem.priority, 4)   // pqueue_test.go:39

firstItem = pqueue.items[2]
firstItemValue, ok = firstItem.value.(string)
assert.True(t, ok)
assert.Equal(t, firstItemValue, "3")
assert.Equal(t, firstItem.priority, 3)

firstItem = pqueue.items[3]
firstItemValue, ok = firstItem.value.(string)
assert.True(t, ok)
assert.Equal(t, firstItemValue, "1")
assert.Equal(t, firstItem.priority, 1)

firstItem = pqueue.items[4]
firstItemValue, ok = firstItem.value.(string)
assert.True(t, ok)
assert.Equal(t, firstItemValue, "2")
assert.Equal(t, firstItem.priority, 2)

}

--- FAIL: TestMaxPQueuePush_protects_max_order (0.00 seconds)
Location: pqueue_test.go:38
Error: Not equal: "3" (expected)
!= "4" (actual)

    Location:       pqueue_test.go:39
Error:      Not equal: 3 (expected)
                != 4 (actual)

    Location:       pqueue_test.go:44
Error:      Not equal: "4" (expected)
                != "3" (actual)

    Location:       pqueue_test.go:45
Error:      Not equal: 4 (expected)
                != 3 (actual)

    Location:       pqueue_test.go:50
Error:      Not equal: "2" (expected)
                != "1" (actual)

    Location:       pqueue_test.go:51
Error:      Not equal: 2 (expected)
                != 1 (actual)

    Location:       pqueue_test.go:56
Error:      Not equal: "1" (expected)
                != "2" (actual)

    Location:       pqueue_test.go:57
Error:      Not equal: 1 (expected)
                != 2 (actual)

Priority queue bug when multiple elements share a priority

If a min priority queue contains three or more elements with the same priority, Head() and Pop() return an unexpected result. e.g.

package main

import (
    "fmt"
    "github.com/oleiade/lane"
)

func main() {
    queue := lane.NewPQueue(lane.MINPQ)
    queue.Push("a", 2)
    queue.Push("b", 2)
    queue.Push("c", 2) // Commenting this out leads to the correct result
    queue.Push("d", 1)
    // expect "d", get "a"
    fmt.Println(queue.Head())
}

"a", "b" and "c" are pushed onto the queue with the same priority (2). "d" is pushed on with a higher priority according to the min-priority queue, so I expect it to become the head. Instead, "a" is returned. The queue behaves correctly if one of the elements with priority 2 is removed.

go version go1.2.2 darwin/amd64

Copy/Contents of Methods

I'd like to use lane to store a variable list of things where the oldest items are only the ones being removed and the newest go in front. Naturally, a queue from this package would work great, but when printing the contents of the queue, you have to destroy it by popping everything off. This makes my brain hurt when trying to figure out whether I can safely print the contents this way while another thread may be writing to the queue, as the locks are per operation (pop), and there's no way to lock the list for the duration of the copy process.

I would like to propose one of two things:

  • A copy method that would generate a new dequeue, stack, etc struct that is completely independent from the source object - this would allow me to store things in my dequeue and copy it before using dequeue repeatedly to print out the contents
  • A contents (or something) method that would return something like an array of the items in the list. This way I could extract the contents of my dequeue to display without modifying the dequeue.

What do you think?

Make Lock/Unlock optional

Hello!
I think that making Lock/Unlock optional would be vital, for instance, if someone will wrap and lock there (wrapper).

if pq.autoLock { pq.Lock() defer pq.Unlock() }

how to loop a queue

when i loop a queue to filter element ,it can't do it.
so i think you should add the loop function

Strange queue behaviour

I tried this code sample based on README:

package main

import "fmt"
import "github.com/oleiade/lane"
import "runtime"


func main() {
    // runtime.GOMAXPROCS(3)

    queue := lane.NewQueue()
    queue.Enqueue("grumpyClient")
    queue.Enqueue("happyClient")
    queue.Enqueue("ecstaticClient")


    // Let's handle the clients asynchronously
    for client := queue.Dequeue(); client != nil; {
        go fmt.Println(client)
    }
}

What I expected from it: should print 3 strings from queue and exit.
What actually happens: prints "grumpyClient" lots of times. Removing "go" in loop does not change anything.

Tested on go1.4.1 darwin/amd64(os x 10.9.5) and go1.2.1 linux/amd64(ubuntu precise). Same thing

Uncommenting line with runtime.GOMAXPROCS(3) causes lots of tracebacks on OS X(not on linux).

Thread safety clarification

Are these data structures safe to manipulate from multiple goroutines, or should we use explicit channels / locks to enforce thread safety?

Queue work failed

@oleiade when i use Queue's example ,the result is endless loop .
the @code:
var queue *Queue = NewQueue()

// Let's add the incoming clients to the queue
queue.Enqueue("grumpyClient")
queue.Enqueue("happyClient")
queue.Enqueue("ecstaticClient")

fmt.Println(queue.Head) // grumpyClient

// Let's handle the clients asynchronously
for client := queue.Dequeue(); client != nil; {
go fmt.Println(client)
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.