-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add circular queue array data structure (#731)
* feat: add circular queue array data structure * test: add missing error handling
- Loading branch information
1 parent
32bb671
commit d7470ba
Showing
2 changed files
with
363 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |