Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add circular queue array data structure #731

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
Loading