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 2 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
199 changes: 199 additions & 0 deletions structure/circularqueue/circularqueue_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
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)
}

queue.Enqueue(1)

Check failure on line 19 in structure/circularqueue/circularqueue_test.go

View workflow job for this annotation

GitHub Actions / Code style and tests

Error return value of `queue.Enqueue` is not checked (errcheck)
queue.Enqueue(2)

Check failure on line 20 in structure/circularqueue/circularqueue_test.go

View workflow job for this annotation

GitHub Actions / Code style and tests

Error return value of `queue.Enqueue` is not checked (errcheck)
queue.Enqueue(3)

Check failure on line 21 in structure/circularqueue/circularqueue_test.go

View workflow job for this annotation

GitHub Actions / Code style and tests

Error return value of `queue.Enqueue` is not checked (errcheck)
queue.Enqueue(4)
queue.Enqueue(5)

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)
}

queue.Dequeue()

Check failure on line 36 in structure/circularqueue/circularqueue_test.go

View workflow job for this annotation

GitHub Actions / Code style and tests

Error return value of `queue.Dequeue` is not checked (errcheck)
queue.Dequeue()

Check failure on line 37 in structure/circularqueue/circularqueue_test.go

View workflow job for this annotation

GitHub Actions / Code style and tests

Error return value of `queue.Dequeue` is not checked (errcheck)

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)

queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)

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)

queue.Enqueue("one")
queue.Enqueue("two")
queue.Enqueue("three")

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)

queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
queue.Dequeue()

Check failure on line 97 in structure/circularqueue/circularqueue_test.go

View workflow job for this annotation

GitHub Actions / Code style and tests

Error return value of `queue.Dequeue` is not checked (errcheck)
queue.Dequeue()
queue.Enqueue(4)
queue.Enqueue(5)
queue.Dequeue()

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)
queue.Enqueue(false)
queue.Enqueue(true)

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

queue.Dequeue()
queue.Dequeue()

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)
}

queue.Enqueue(1.0)

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)

queue.Enqueue('a')
queue.Enqueue('b')
queue.Enqueue('c')

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++ {
queue.Enqueue(i)
}
})

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

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

Check failure on line 196 in structure/circularqueue/circularqueue_test.go

View workflow job for this annotation

GitHub Actions / Code style and tests

Error return value of `queue.Peek` is not checked (errcheck)
}
})
}
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