Skip to content

Commit

Permalink
feat: add circular queue array data structure (#731)
Browse files Browse the repository at this point in the history
* feat: add circular queue array data structure

* test: add missing error handling
  • Loading branch information
aramceballos authored Aug 11, 2024
1 parent 32bb671 commit d7470ba
Show file tree
Hide file tree
Showing 2 changed files with 363 additions and 0 deletions.
267 changes: 267 additions & 0 deletions structure/circularqueue/circularqueue_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
package circularqueue

import "testing"

func TestCircularQueue(t *testing.T) {
t.Run("Size Check", func(t *testing.T) {
_, err := NewCircularQueue[int](-3)
if err == nil {
t.Errorf("Expected error, got nil")
}

queue, _ := NewCircularQueue[int](5)
expectedSize := 5
gotSize := queue.Size()
if gotSize != expectedSize {
t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize)
}

if err := queue.Enqueue(1); err != nil {
t.Error(err)
}
if err := queue.Enqueue(2); err != nil {
t.Error(err)
}
if err := queue.Enqueue(3); err != nil {
t.Error(err)
}
if err := queue.Enqueue(4); err != nil {
t.Error(err)
}
if err := queue.Enqueue(5); err != nil {
t.Error(err)
}

err = queue.Enqueue(6)
if err == nil {
t.Errorf("Expected error, got nil")
}

expectedSize = 5
gotSize = queue.Size()
if gotSize != expectedSize {
t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize)
}

if _, err := queue.Dequeue(); err != nil {
t.Error(err)
}
if _, err := queue.Dequeue(); err != nil {
t.Error(err)
}

err = queue.Enqueue(6)
if err != nil {
t.Errorf("Expected nil, got error: %v\n", err.Error())
}

expectedSize = 5
gotSize = queue.Size()
if gotSize != expectedSize {
t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize)
}
})
t.Run("Enqueue", func(t *testing.T) {
queue, _ := NewCircularQueue[int](10)

if err := queue.Enqueue(1); err != nil {
t.Error(err)
}
if err := queue.Enqueue(2); err != nil {
t.Error(err)
}
if err := queue.Enqueue(3); err != nil {
t.Error(err)
}

expected := 1
got, err := queue.Peek()
if err != nil {
t.Error(err.Error())
}
if got != expected {
t.Errorf("Expected: %v got: %v\n", expected, got)
}
})
t.Run("Dequeue", func(t *testing.T) {
queue, _ := NewCircularQueue[string](10)

if err := queue.Enqueue("one"); err != nil {
t.Error(err)
}
if err := queue.Enqueue("two"); err != nil {
t.Error(err)
}
if err := queue.Enqueue("three"); err != nil {
t.Error(err)
}

expected := "one"
got, err := queue.Dequeue()
if err != nil {
t.Error(err.Error())
}
if got != expected {
t.Errorf("Expected: %v got: %v\n", expected, got)
}

expected = "two"
got, err = queue.Peek()
if err != nil {
t.Error(err.Error())
}
if got != expected {
t.Errorf("Expected: %v got: %v\n", expected, got)
}
})
t.Run("Circularity", func(t *testing.T) {
queue, _ := NewCircularQueue[int](10)

if err := queue.Enqueue(1); err != nil {
t.Error(err)
}
if err := queue.Enqueue(2); err != nil {
t.Error(err)
}
if err := queue.Enqueue(3); err != nil {
t.Error(err)
}
if _, err := queue.Dequeue(); err != nil {
t.Error(err)
}
if _, err := queue.Dequeue(); err != nil {
t.Error(err)
}
if err := queue.Enqueue(4); err != nil {
t.Error(err)
}
if err := queue.Enqueue(5); err != nil {
t.Error(err)
}
if _, err := queue.Dequeue(); err != nil {
t.Error(err)
}

expected := 4
got, err := queue.Peek()
if err != nil {
t.Error(err.Error())
}
if got != expected {
t.Errorf("Expected: %v got: %v\n", expected, got)
}
})
t.Run("IsFull", func(t *testing.T) {
queue, _ := NewCircularQueue[bool](2)
if err := queue.Enqueue(false); err != nil {
t.Error(err)
}
if err := queue.Enqueue(true); err != nil {
t.Error(err)
}

expected := true
got := queue.IsFull()
if got != expected {
t.Errorf("Expected: %v got: %v\n", expected, got)
}

if _, err := queue.Dequeue(); err != nil {
t.Error(err)
}
if _, err := queue.Dequeue(); err != nil {
t.Error(err)
}

expected = false
got = queue.IsFull()
if got != expected {
t.Errorf("Expected: %v got: %v\n", expected, got)
}
})
t.Run("IsEmpty", func(t *testing.T) {
queue, _ := NewCircularQueue[float64](2)

expected := true
got := queue.IsEmpty()
if got != expected {
t.Errorf("Expected: %v got: %v\n", expected, got)
}

if err := queue.Enqueue(1.0); err != nil {
t.Error(err)
}

expected = false
got = queue.IsEmpty()
if got != expected {
t.Errorf("Expected: %v got: %v\n", expected, got)
}

})
t.Run("Peak", func(t *testing.T) {
queue, _ := NewCircularQueue[rune](10)

if err := queue.Enqueue('a'); err != nil {
t.Error(err)
}
if err := queue.Enqueue('b'); err != nil {
t.Error(err)
}
if err := queue.Enqueue('c'); err != nil {
t.Error(err)
}

expected := 'a'
got, err := queue.Peek()
if err != nil {
t.Error(err.Error())
}

if got != expected {
t.Errorf("Expected: %v got: %v\n", expected, got)
}
})
}

// BenchmarkCircularQueue benchmarks the CircularQueue implementation.
func BenchmarkCircularQueue(b *testing.B) {
b.Run("Enqueue", func(b *testing.B) {
queue, _ := NewCircularQueue[int](1000)
for i := 0; i < b.N; i++ {
if err := queue.Enqueue(i); err != nil {
b.Error(err)
}
}
})

b.Run("Dequeue", func(b *testing.B) {
queue, _ := NewCircularQueue[int](1000)
for i := 0; i < 1000; i++ {
if err := queue.Enqueue(i); err != nil {
b.Error(err)
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if _, err := queue.Dequeue(); err != nil {
b.Error(err)
}
}
})

b.Run("Peek", func(b *testing.B) {
queue, _ := NewCircularQueue[int](1000)
for i := 0; i < 1000; i++ {
if err := queue.Enqueue(i); err != nil {
b.Error(err)
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if _, err := queue.Peek(); err != nil {
b.Error(err)
}
}
})
}
96 changes: 96 additions & 0 deletions structure/circularqueue/circularqueuearray.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// circularqueuearray.go
// description: Implementation of a circular queue data structure
// details:
// This file contains the implementation of a circular queue data structure
// using generics in Go. The circular queue supports basic operations such as
// enqueue, dequeue, peek, and checks for full and empty states.
// author(s): [Aram Ceballos](https://github.com/aramceballos)
// ref: https://www.programiz.com/dsa/circular-queue
// ref: https://en.wikipedia.org/wiki/Circular_buffer

// Package queue provides an implementation of a circular queue data structure.
package circularqueue

// errors package: Provides functions to create and manipulate error values
import (
"errors"
)

// CircularQueue represents a circular queue data structure.
type CircularQueue[T any] struct {
items []T
front int
rear int
size int
}

// NewCircularQueue creates a new CircularQueue with the given size.
// Returns an error if the size is less than or equal to 0.
func NewCircularQueue[T any](size int) (*CircularQueue[T], error) {
if size <= 0 {
return nil, errors.New("size must be greater than 0")
}
return &CircularQueue[T]{
items: make([]T, size),
front: -1,
rear: -1,
size: size,
}, nil
}

// Enqueue adds an item to the rear of the queue.
// Returns an error if the queue is full.
func (cq *CircularQueue[T]) Enqueue(item T) error {
if cq.IsFull() {
return errors.New("queue is full")
}
if cq.IsEmpty() {
cq.front = 0
}
cq.rear = (cq.rear + 1) % cq.size
cq.items[cq.rear] = item
return nil
}

// Dequeue removes and returns the item from the front of the queue.
// Returns an error if the queue is empty.
func (cq *CircularQueue[T]) Dequeue() (T, error) {
if cq.IsEmpty() {
var zeroValue T
return zeroValue, errors.New("queue is empty")
}
retVal := cq.items[cq.front]
if cq.front == cq.rear {
cq.front = -1
cq.rear = -1
} else {
cq.front = (cq.front + 1) % cq.size
}
return retVal, nil
}

// IsFull checks if the queue is full.
func (cq *CircularQueue[T]) IsFull() bool {
return (cq.front == 0 && cq.rear == cq.size-1) || cq.front == cq.rear+1
}

// IsEmpty checks if the queue is empty.
func (cq *CircularQueue[T]) IsEmpty() bool {
return cq.front == -1 && cq.rear == -1
}

// Peek returns the item at the front of the queue without removing it.
// Returns an error if the queue is empty.
func (cq *CircularQueue[T]) Peek() (T, error) {
if cq.IsEmpty() {
var zeroValue T
return zeroValue, errors.New("queue is empty")
}

return cq.items[cq.front], nil
}

// Size returns the size of the queue.
func (cq *CircularQueue[T]) Size() int {
return cq.size
}

0 comments on commit d7470ba

Please sign in to comment.