Skip to content

Commit

Permalink
Merge pull request #203 from emirpasic/development
Browse files Browse the repository at this point in the history
Implements PriorityQueue and CircularBuffer
  • Loading branch information
emirpasic committed Apr 14, 2022
2 parents da429eb + 2bf1bd3 commit 773505c
Show file tree
Hide file tree
Showing 14 changed files with 1,972 additions and 59 deletions.
128 changes: 107 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ Implementation of various data structures and algorithms in Go.
- [Queues](#queues)
- [LinkedListQueue](#linkedlistqueue)
- [ArrayQueue](#arrayqueue)
- [CircularBuffer](#circularbuffer)
- [PriorityQueue](#priorityqueue)
- [Functions](#functions)
- [Comparator](#comparator)
- [Iterator](#iterator)
Expand Down Expand Up @@ -67,40 +69,42 @@ type Container interface {
Size() int
Clear()
Values() []interface{}
String() string
String() string
}
```

Containers are either ordered or unordered. All ordered containers provide [stateful iterators](#iterator) and some of them allow [enumerable functions](#enumerable).

| **Data** | **Structure** | **Ordered** | **[Iterator](#iterator)** | **[Enumerable](#enumerable)** | **Referenced by** |
| :--- | :--- | :---: | :---: | :---: | :---: |
| **Data** | **Structure** | **Ordered** | **[Iterator](#iterator)** | **[Enumerable](#enumerable)** | **Referenced by** |
| :--- |:--------------------------------------| :---: | :---: | :---: | :---: |
| [Lists](#lists) |
| | [ArrayList](#arraylist) | yes | yes* | yes | index |
| | [ArrayList](#arraylist) | yes | yes* | yes | index |
| | [SinglyLinkedList](#singlylinkedlist) | yes | yes | yes | index |
| | [DoublyLinkedList](#doublylinkedlist) | yes | yes* | yes | index |
| [Sets](#sets) |
| | [HashSet](#hashset) | no | no | no | index |
| | [TreeSet](#treeset) | yes | yes* | yes | index |
| | [LinkedHashSet](#linkedhashset) | yes | yes* | yes | index |
| | [HashSet](#hashset) | no | no | no | index |
| | [TreeSet](#treeset) | yes | yes* | yes | index |
| | [LinkedHashSet](#linkedhashset) | yes | yes* | yes | index |
| [Stacks](#stacks) |
| | [LinkedListStack](#linkedliststack) | yes | yes | no | index |
| | [ArrayStack](#arraystack) | yes | yes* | no | index |
| | [LinkedListStack](#linkedliststack) | yes | yes | no | index |
| | [ArrayStack](#arraystack) | yes | yes* | no | index |
| [Maps](#maps) |
| | [HashMap](#hashmap) | no | no | no | key |
| | [TreeMap](#treemap) | yes | yes* | yes | key |
| | [LinkedHashMap](#linkedhashmap) | yes | yes* | yes | key |
| | [HashBidiMap](#hashbidimap) | no | no | no | key* |
| | [TreeBidiMap](#treebidimap) | yes | yes* | yes | key* |
| | [HashMap](#hashmap) | no | no | no | key |
| | [TreeMap](#treemap) | yes | yes* | yes | key |
| | [LinkedHashMap](#linkedhashmap) | yes | yes* | yes | key |
| | [HashBidiMap](#hashbidimap) | no | no | no | key* |
| | [TreeBidiMap](#treebidimap) | yes | yes* | yes | key* |
| [Trees](#trees) |
| | [RedBlackTree](#redblacktree) | yes | yes* | no | key |
| | [AVLTree](#avltree) | yes | yes* | no | key |
| | [BTree](#btree) | yes | yes* | no | key |
| | [BinaryHeap](#binaryheap) | yes | yes* | no | index |
| | [RedBlackTree](#redblacktree) | yes | yes* | no | key |
| | [AVLTree](#avltree) | yes | yes* | no | key |
| | [BTree](#btree) | yes | yes* | no | key |
| | [BinaryHeap](#binaryheap) | yes | yes* | no | index |
| [Queues](#queues) |
| | [LinkedListQueue](#linkedlistqueue) | yes | yes | no | index |
| | [ArrayQueue](#arrayqueue) | yes | yes* | no | index |
| | | | <sub><sup>*reversible</sup></sub> | | <sub><sup>*bidirectional</sup></sub> |
| | [LinkedListQueue](#linkedlistqueue) | yes | yes | no | index |
| | [ArrayQueue](#arrayqueue) | yes | yes* | no | index |
| | [CircularBuffer](#circularbuffer) | yes | yes* | no | index |
| | [PriorityQueue](#priorityqueue) | yes | yes* | no | index |
| | | | <sub><sup>*reversible</sup></sub> | | <sub><sup>*bidirectional</sup></sub> |

### Lists

Expand Down Expand Up @@ -949,6 +953,88 @@ func main() {
}
```

#### CircularBuffer

A circular buffer, circular [queue](#queues), cyclic buffer or ring buffer is a data structure that uses a single, fixed-size buffer as if it were connected end-to-end. This structure lends itself easily to buffering data streams.

<p align="center"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fd/Circular_Buffer_Animation.gif/400px-Circular_Buffer_Animation.gif" width="300px" height="300px" /></p>

Implements [Queue](#queues), [ReverseIteratorWithIndex](#iteratorwithindex), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

```go
package main

import cb "github.com/emirpasic/gods/queues/circularbuffer"

// CircularBufferExample to demonstrate basic usage of CircularBuffer
func main() {
queue := cb.New(3) // empty (max size is 3)
queue.Enqueue(1) // 1
queue.Enqueue(2) // 1, 2
queue.Enqueue(3) // 1, 2, 3
_ = queue.Values() // 1, 2, 3
queue.Enqueue(3) // 4, 2, 3
_, _ = queue.Peek() // 4,true
_, _ = queue.Dequeue() // 4, true
_, _ = queue.Dequeue() // 2, true
_, _ = queue.Dequeue() // 3, true
_, _ = queue.Dequeue() // nil, false (nothing to deque)
queue.Enqueue(1) // 1
queue.Clear() // empty
queue.Empty() // true
_ = queue.Size() // 0
}
```

#### PriorityQueue

A priority queue is a special type of [queue](#queues) in which each element is associated with a priority value. And, elements are served on the basis of their priority. That is, higher priority elements are served first. However, if elements with the same priority occur, they are served according to their order in the queue.

Implements [Queue](#queues), [ReverseIteratorWithIndex](#iteratorwithindex), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.

```go
package main

import (
pq "github.com/emirpasic/gods/queues/priorityqueue"
"github.com/emirpasic/gods/utils"
)

// Element is an entry in the priority queue
type Element struct {
name string
priority int
}

// Comparator function (sort by element's priority value in descending order)
func byPriority(a, b interface{}) int {
priorityA := a.(Element).priority
priorityB := b.(Element).priority
return -utils.IntComparator(priorityA, priorityB) // "-" descending order
}

// PriorityQueueExample to demonstrate basic usage of BinaryHeap
func main() {
a := Element{name: "a", priority: 1}
b := Element{name: "b", priority: 2}
c := Element{name: "c", priority: 3}

queue := pq.NewWith(byPriority) // empty
queue.Enqueue(a) // {a 1}
queue.Enqueue(c) // {c 3}, {a 1}
queue.Enqueue(b) // {c 3}, {b 2}, {a 1}
_ = queue.Values() // [{c 3} {b 2} {a 1}]
_, _ = queue.Peek() // {c 3} true
_, _ = queue.Dequeue() // {c 3} true
_, _ = queue.Dequeue() // {b 2} true
_, _ = queue.Dequeue() // {a 1} true
_, _ = queue.Dequeue() // <nil> false (nothing to dequeue)
queue.Clear() // empty
_ = queue.Empty() // true
_ = queue.Size() // 0
}
```

## Functions

Various helper functions used throughout the library.
Expand Down
26 changes: 26 additions & 0 deletions examples/circularbuffer/circularbuffer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import cb "github.com/emirpasic/gods/queues/circularbuffer"

// CircularBufferExample to demonstrate basic usage of CircularBuffer
func main() {
queue := cb.New(3) // empty (max size is 3)
queue.Enqueue(1) // 1
queue.Enqueue(2) // 1, 2
queue.Enqueue(3) // 1, 2, 3
_ = queue.Values() // 1, 2, 3
queue.Enqueue(3) // 4, 2, 3
_, _ = queue.Peek() // 4,true
_, _ = queue.Dequeue() // 4, true
_, _ = queue.Dequeue() // 2, true
_, _ = queue.Dequeue() // 3, true
_, _ = queue.Dequeue() // nil, false (nothing to deque)
queue.Enqueue(1) // 1
queue.Clear() // empty
queue.Empty() // true
_ = queue.Size() // 0
}
44 changes: 44 additions & 0 deletions examples/priorityqueue/priorityqueue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
pq "github.com/emirpasic/gods/queues/priorityqueue"
"github.com/emirpasic/gods/utils"
)

// Element is an entry in the priority queue
type Element struct {
name string
priority int
}

// Comparator function (sort by element's priority value in descending order)
func byPriority(a, b interface{}) int {
priorityA := a.(Element).priority
priorityB := b.(Element).priority
return -utils.IntComparator(priorityA, priorityB) // "-" descending order
}

// PriorityQueueExample to demonstrate basic usage of BinaryHeap
func main() {
a := Element{name: "a", priority: 1}
b := Element{name: "b", priority: 2}
c := Element{name: "c", priority: 3}

queue := pq.NewWith(byPriority) // empty
queue.Enqueue(a) // {a 1}
queue.Enqueue(c) // {c 3}, {a 1}
queue.Enqueue(b) // {c 3}, {b 2}, {a 1}
_ = queue.Values() // [{c 3} {b 2} {a 1}]
_, _ = queue.Peek() // {c 3} true
_, _ = queue.Dequeue() // {c 3} true
_, _ = queue.Dequeue() // {b 2} true
_, _ = queue.Dequeue() // {a 1} true
_, _ = queue.Dequeue() // <nil> false (nothing to dequeue)
queue.Clear() // empty
_ = queue.Empty() // true
_ = queue.Size() // 0
}
153 changes: 153 additions & 0 deletions queues/circularbuffer/circularbuffer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright (c) 2021, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package circularbuffer implements the circular buffer.
//
// In computer science, a circular buffer, circular queue, cyclic buffer or ring buffer is a data structure that uses a single, fixed-size buffer as if it were connected end-to-end. This structure lends itself easily to buffering data streams.
//
// Structure is not thread safe.
//
// Reference: https://en.wikipedia.org/wiki/Circular_buffer
package circularbuffer

import (
"fmt"
"strings"

"github.com/emirpasic/gods/queues"
)

// Assert Queue implementation
var _ queues.Queue = (*Queue)(nil)

// Queue holds values in a slice.
type Queue struct {
values []interface{}
start int
end int
full bool
maxSize int
size int
}

// New instantiates a new empty queue with the specified size of maximum number of elements that it can hold.
// This max size of the buffer cannot be changed.
func New(maxSize int) *Queue {
if maxSize < 1 {
panic("Invalid maxSize, should be at least 1")
}
queue := &Queue{maxSize: maxSize}
queue.Clear()
return queue
}

// Enqueue adds a value to the end of the queue
func (queue *Queue) Enqueue(value interface{}) {
if queue.Full() {
queue.Dequeue()
}
queue.values[queue.end] = value
queue.end = queue.end + 1
if queue.end >= queue.maxSize {
queue.end = 0
}
if queue.end == queue.start {
queue.full = true
}

queue.size = queue.calculateSize()
}

// Dequeue removes first element of the queue and returns it, or nil if queue is empty.
// Second return parameter is true, unless the queue was empty and there was nothing to dequeue.
func (queue *Queue) Dequeue() (value interface{}, ok bool) {
if queue.Empty() {
return nil, false
}

value, ok = queue.values[queue.start], true

if value != nil {
queue.values[queue.start] = nil
queue.start = queue.start + 1
if queue.start >= queue.maxSize {
queue.start = 0
}
queue.full = false
}

queue.size = queue.size - 1

return
}

// Peek returns first element of the queue without removing it, or nil if queue is empty.
// Second return parameter is true, unless the queue was empty and there was nothing to peek.
func (queue *Queue) Peek() (value interface{}, ok bool) {
if queue.Empty() {
return nil, false
}
return queue.values[queue.start], true
}

// Empty returns true if queue does not contain any elements.
func (queue *Queue) Empty() bool {
return queue.Size() == 0
}

// Full returns true if the queue is full, i.e. has reached the maximum number of elements that it can hold.
func (queue *Queue) Full() bool {
return queue.Size() == queue.maxSize
}

// Size returns number of elements within the queue.
func (queue *Queue) Size() int {
return queue.size
}

// Clear removes all elements from the queue.
func (queue *Queue) Clear() {
queue.values = make([]interface{}, queue.maxSize, queue.maxSize)
queue.start = 0
queue.end = 0
queue.full = false
queue.size = 0
}

// Values returns all elements in the queue (FIFO order).
func (queue *Queue) Values() []interface{} {
values := make([]interface{}, queue.Size(), queue.Size())
for i := 0; i < queue.Size(); i++ {
values[i] = queue.values[(queue.start+i)%queue.maxSize]
}
return values
}

// String returns a string representation of container
func (queue *Queue) String() string {
str := "CircularBuffer\n"
var values []string
for _, value := range queue.Values() {
values = append(values, fmt.Sprintf("%v", value))
}
str += strings.Join(values, ", ")
return str
}

// Check that the index is within bounds of the list
func (queue *Queue) withinRange(index int) bool {
return index >= 0 && index < queue.size
}

func (queue *Queue) calculateSize() int {
if queue.end < queue.start {
return queue.maxSize - queue.start + queue.end
} else if queue.end == queue.start {
if queue.full {
return queue.maxSize
}
return 0
}
return queue.end - queue.start
}
Loading

0 comments on commit 773505c

Please sign in to comment.