Skip to content

Commit d876ac8

Browse files
committed
feat: add circular queue array data structure
1 parent ee6fef2 commit d876ac8

File tree

2 files changed

+295
-0
lines changed

2 files changed

+295
-0
lines changed
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package circularqueue
2+
3+
import "testing"
4+
5+
func TestCircularQueue(t *testing.T) {
6+
t.Run("Size Check", func(t *testing.T) {
7+
_, err := NewCircularQueue[int](-3)
8+
if err == nil {
9+
t.Errorf("Expected error, got nil")
10+
}
11+
12+
queue, _ := NewCircularQueue[int](5)
13+
expectedSize := 5
14+
gotSize := queue.Size()
15+
if gotSize != expectedSize {
16+
t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize)
17+
}
18+
19+
queue.Enqueue(1)
20+
queue.Enqueue(2)
21+
queue.Enqueue(3)
22+
queue.Enqueue(4)
23+
queue.Enqueue(5)
24+
25+
err = queue.Enqueue(6)
26+
if err == nil {
27+
t.Errorf("Expected error, got nil")
28+
}
29+
30+
expectedSize = 5
31+
gotSize = queue.Size()
32+
if gotSize != expectedSize {
33+
t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize)
34+
}
35+
36+
queue.Dequeue()
37+
queue.Dequeue()
38+
39+
err = queue.Enqueue(6)
40+
if err != nil {
41+
t.Errorf("Expected nil, got error: %v\n", err.Error())
42+
}
43+
44+
expectedSize = 5
45+
gotSize = queue.Size()
46+
if gotSize != expectedSize {
47+
t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize)
48+
}
49+
})
50+
t.Run("Enqueue", func(t *testing.T) {
51+
queue, _ := NewCircularQueue[int](10)
52+
53+
queue.Enqueue(1)
54+
queue.Enqueue(2)
55+
queue.Enqueue(3)
56+
57+
expected := 1
58+
got, err := queue.Peek()
59+
if err != nil {
60+
t.Error(err.Error())
61+
}
62+
if got != expected {
63+
t.Errorf("Expected: %v got: %v\n", expected, got)
64+
}
65+
})
66+
t.Run("Dequeue", func(t *testing.T) {
67+
queue, _ := NewCircularQueue[string](10)
68+
69+
queue.Enqueue("one")
70+
queue.Enqueue("two")
71+
queue.Enqueue("three")
72+
73+
expected := "one"
74+
got, err := queue.Dequeue()
75+
if err != nil {
76+
t.Error(err.Error())
77+
}
78+
if got != expected {
79+
t.Errorf("Expected: %v got: %v\n", expected, got)
80+
}
81+
82+
expected = "two"
83+
got, err = queue.Peek()
84+
if err != nil {
85+
t.Error(err.Error())
86+
}
87+
if got != expected {
88+
t.Errorf("Expected: %v got: %v\n", expected, got)
89+
}
90+
})
91+
t.Run("Circularity", func(t *testing.T) {
92+
queue, _ := NewCircularQueue[int](10)
93+
94+
queue.Enqueue(1)
95+
queue.Enqueue(2)
96+
queue.Enqueue(3)
97+
queue.Dequeue()
98+
queue.Dequeue()
99+
queue.Enqueue(4)
100+
queue.Enqueue(5)
101+
queue.Dequeue()
102+
103+
expected := 4
104+
got, err := queue.Peek()
105+
if err != nil {
106+
t.Error(err.Error())
107+
}
108+
if got != expected {
109+
t.Errorf("Expected: %v got: %v\n", expected, got)
110+
}
111+
})
112+
t.Run("IsFull", func(t *testing.T) {
113+
queue, _ := NewCircularQueue[bool](2)
114+
queue.Enqueue(false)
115+
queue.Enqueue(true)
116+
117+
expected := true
118+
got := queue.IsFull()
119+
if got != expected {
120+
t.Errorf("Expected: %v got: %v\n", expected, got)
121+
}
122+
123+
queue.Dequeue()
124+
queue.Dequeue()
125+
126+
expected = false
127+
got = queue.IsFull()
128+
if got != expected {
129+
t.Errorf("Expected: %v got: %v\n", expected, got)
130+
}
131+
})
132+
t.Run("IsEmpty", func(t *testing.T) {
133+
queue, _ := NewCircularQueue[float64](2)
134+
135+
expected := true
136+
got := queue.IsEmpty()
137+
if got != expected {
138+
t.Errorf("Expected: %v got: %v\n", expected, got)
139+
}
140+
141+
queue.Enqueue(1.0)
142+
143+
expected = false
144+
got = queue.IsEmpty()
145+
if got != expected {
146+
t.Errorf("Expected: %v got: %v\n", expected, got)
147+
}
148+
149+
})
150+
t.Run("Peak", func(t *testing.T) {
151+
queue, _ := NewCircularQueue[rune](10)
152+
153+
queue.Enqueue('a')
154+
queue.Enqueue('b')
155+
queue.Enqueue('c')
156+
157+
expected := 'a'
158+
got, err := queue.Peek()
159+
if err != nil {
160+
t.Error(err.Error())
161+
}
162+
163+
if got != expected {
164+
t.Errorf("Expected: %v got: %v\n", expected, got)
165+
}
166+
})
167+
}
168+
169+
// BenchmarkCircularQueue benchmarks the CircularQueue implementation.
170+
func BenchmarkCircularQueue(b *testing.B) {
171+
b.Run("Enqueue", func(b *testing.B) {
172+
queue, _ := NewCircularQueue[int](1000)
173+
for i := 0; i < b.N; i++ {
174+
queue.Enqueue(i)
175+
}
176+
})
177+
178+
b.Run("Dequeue", func(b *testing.B) {
179+
queue, _ := NewCircularQueue[int](1000)
180+
for i := 0; i < 1000; i++ {
181+
queue.Enqueue(i)
182+
}
183+
b.ResetTimer()
184+
for i := 0; i < b.N; i++ {
185+
queue.Dequeue()
186+
}
187+
})
188+
189+
b.Run("Peek", func(b *testing.B) {
190+
queue, _ := NewCircularQueue[int](1000)
191+
for i := 0; i < 1000; i++ {
192+
queue.Enqueue(i)
193+
}
194+
b.ResetTimer()
195+
for i := 0; i < b.N; i++ {
196+
queue.Peek()
197+
}
198+
})
199+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// circularqueuearray.go
2+
// description: Implementation of a circular queue data structure
3+
// details:
4+
// This file contains the implementation of a circular queue data structure
5+
// using generics in Go. The circular queue supports basic operations such as
6+
// enqueue, dequeue, peek, and checks for full and empty states.
7+
// author(s): [Aram Ceballos](https://github.com/aramceballos)
8+
// ref: https://www.programiz.com/dsa/circular-queue
9+
// ref: https://en.wikipedia.org/wiki/Circular_buffer
10+
11+
// Package queue provides an implementation of a circular queue data structure.
12+
package circularqueue
13+
14+
// errors package: Provides functions to create and manipulate error values
15+
import (
16+
"errors"
17+
)
18+
19+
// CircularQueue represents a circular queue data structure.
20+
type CircularQueue[T any] struct {
21+
items []T
22+
front int
23+
rear int
24+
size int
25+
}
26+
27+
// NewCircularQueue creates a new CircularQueue with the given size.
28+
// Returns an error if the size is less than or equal to 0.
29+
func NewCircularQueue[T any](size int) (*CircularQueue[T], error) {
30+
if size <= 0 {
31+
return nil, errors.New("size must be greater than 0")
32+
}
33+
return &CircularQueue[T]{
34+
items: make([]T, size),
35+
front: -1,
36+
rear: -1,
37+
size: size,
38+
}, nil
39+
}
40+
41+
// Enqueue adds an item to the rear of the queue.
42+
// Returns an error if the queue is full.
43+
func (cq *CircularQueue[T]) Enqueue(item T) error {
44+
if cq.IsFull() {
45+
return errors.New("queue is full")
46+
}
47+
if cq.IsEmpty() {
48+
cq.front = 0
49+
}
50+
cq.rear = (cq.rear + 1) % cq.size
51+
cq.items[cq.rear] = item
52+
return nil
53+
}
54+
55+
// Dequeue removes and returns the item from the front of the queue.
56+
// Returns an error if the queue is empty.
57+
func (cq *CircularQueue[T]) Dequeue() (T, error) {
58+
if cq.IsEmpty() {
59+
var zeroValue T
60+
return zeroValue, errors.New("queue is empty")
61+
}
62+
retVal := cq.items[cq.front]
63+
if cq.front == cq.rear {
64+
cq.front = -1
65+
cq.rear = -1
66+
} else {
67+
cq.front = (cq.front + 1) % cq.size
68+
}
69+
return retVal, nil
70+
}
71+
72+
// IsFull checks if the queue is full.
73+
func (cq *CircularQueue[T]) IsFull() bool {
74+
return (cq.front == 0 && cq.rear == cq.size-1) || cq.front == cq.rear+1
75+
}
76+
77+
// IsEmpty checks if the queue is empty.
78+
func (cq *CircularQueue[T]) IsEmpty() bool {
79+
return cq.front == -1 && cq.rear == -1
80+
}
81+
82+
// Peek returns the item at the front of the queue without removing it.
83+
// Returns an error if the queue is empty.
84+
func (cq *CircularQueue[T]) Peek() (T, error) {
85+
if cq.IsEmpty() {
86+
var zeroValue T
87+
return zeroValue, errors.New("queue is empty")
88+
}
89+
90+
return cq.items[cq.front], nil
91+
}
92+
93+
// Size returns the size of the queue.
94+
func (cq *CircularQueue[T]) Size() int {
95+
return cq.size
96+
}

0 commit comments

Comments
 (0)