Skip to content

Commit d7470ba

Browse files
authored
feat: add circular queue array data structure (#731)
* feat: add circular queue array data structure * test: add missing error handling
1 parent 32bb671 commit d7470ba

File tree

2 files changed

+363
-0
lines changed

2 files changed

+363
-0
lines changed
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
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+
if err := queue.Enqueue(1); err != nil {
20+
t.Error(err)
21+
}
22+
if err := queue.Enqueue(2); err != nil {
23+
t.Error(err)
24+
}
25+
if err := queue.Enqueue(3); err != nil {
26+
t.Error(err)
27+
}
28+
if err := queue.Enqueue(4); err != nil {
29+
t.Error(err)
30+
}
31+
if err := queue.Enqueue(5); err != nil {
32+
t.Error(err)
33+
}
34+
35+
err = queue.Enqueue(6)
36+
if err == nil {
37+
t.Errorf("Expected error, got nil")
38+
}
39+
40+
expectedSize = 5
41+
gotSize = queue.Size()
42+
if gotSize != expectedSize {
43+
t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize)
44+
}
45+
46+
if _, err := queue.Dequeue(); err != nil {
47+
t.Error(err)
48+
}
49+
if _, err := queue.Dequeue(); err != nil {
50+
t.Error(err)
51+
}
52+
53+
err = queue.Enqueue(6)
54+
if err != nil {
55+
t.Errorf("Expected nil, got error: %v\n", err.Error())
56+
}
57+
58+
expectedSize = 5
59+
gotSize = queue.Size()
60+
if gotSize != expectedSize {
61+
t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize)
62+
}
63+
})
64+
t.Run("Enqueue", func(t *testing.T) {
65+
queue, _ := NewCircularQueue[int](10)
66+
67+
if err := queue.Enqueue(1); err != nil {
68+
t.Error(err)
69+
}
70+
if err := queue.Enqueue(2); err != nil {
71+
t.Error(err)
72+
}
73+
if err := queue.Enqueue(3); err != nil {
74+
t.Error(err)
75+
}
76+
77+
expected := 1
78+
got, err := queue.Peek()
79+
if err != nil {
80+
t.Error(err.Error())
81+
}
82+
if got != expected {
83+
t.Errorf("Expected: %v got: %v\n", expected, got)
84+
}
85+
})
86+
t.Run("Dequeue", func(t *testing.T) {
87+
queue, _ := NewCircularQueue[string](10)
88+
89+
if err := queue.Enqueue("one"); err != nil {
90+
t.Error(err)
91+
}
92+
if err := queue.Enqueue("two"); err != nil {
93+
t.Error(err)
94+
}
95+
if err := queue.Enqueue("three"); err != nil {
96+
t.Error(err)
97+
}
98+
99+
expected := "one"
100+
got, err := queue.Dequeue()
101+
if err != nil {
102+
t.Error(err.Error())
103+
}
104+
if got != expected {
105+
t.Errorf("Expected: %v got: %v\n", expected, got)
106+
}
107+
108+
expected = "two"
109+
got, err = queue.Peek()
110+
if err != nil {
111+
t.Error(err.Error())
112+
}
113+
if got != expected {
114+
t.Errorf("Expected: %v got: %v\n", expected, got)
115+
}
116+
})
117+
t.Run("Circularity", func(t *testing.T) {
118+
queue, _ := NewCircularQueue[int](10)
119+
120+
if err := queue.Enqueue(1); err != nil {
121+
t.Error(err)
122+
}
123+
if err := queue.Enqueue(2); err != nil {
124+
t.Error(err)
125+
}
126+
if err := queue.Enqueue(3); err != nil {
127+
t.Error(err)
128+
}
129+
if _, err := queue.Dequeue(); err != nil {
130+
t.Error(err)
131+
}
132+
if _, err := queue.Dequeue(); err != nil {
133+
t.Error(err)
134+
}
135+
if err := queue.Enqueue(4); err != nil {
136+
t.Error(err)
137+
}
138+
if err := queue.Enqueue(5); err != nil {
139+
t.Error(err)
140+
}
141+
if _, err := queue.Dequeue(); err != nil {
142+
t.Error(err)
143+
}
144+
145+
expected := 4
146+
got, err := queue.Peek()
147+
if err != nil {
148+
t.Error(err.Error())
149+
}
150+
if got != expected {
151+
t.Errorf("Expected: %v got: %v\n", expected, got)
152+
}
153+
})
154+
t.Run("IsFull", func(t *testing.T) {
155+
queue, _ := NewCircularQueue[bool](2)
156+
if err := queue.Enqueue(false); err != nil {
157+
t.Error(err)
158+
}
159+
if err := queue.Enqueue(true); err != nil {
160+
t.Error(err)
161+
}
162+
163+
expected := true
164+
got := queue.IsFull()
165+
if got != expected {
166+
t.Errorf("Expected: %v got: %v\n", expected, got)
167+
}
168+
169+
if _, err := queue.Dequeue(); err != nil {
170+
t.Error(err)
171+
}
172+
if _, err := queue.Dequeue(); err != nil {
173+
t.Error(err)
174+
}
175+
176+
expected = false
177+
got = queue.IsFull()
178+
if got != expected {
179+
t.Errorf("Expected: %v got: %v\n", expected, got)
180+
}
181+
})
182+
t.Run("IsEmpty", func(t *testing.T) {
183+
queue, _ := NewCircularQueue[float64](2)
184+
185+
expected := true
186+
got := queue.IsEmpty()
187+
if got != expected {
188+
t.Errorf("Expected: %v got: %v\n", expected, got)
189+
}
190+
191+
if err := queue.Enqueue(1.0); err != nil {
192+
t.Error(err)
193+
}
194+
195+
expected = false
196+
got = queue.IsEmpty()
197+
if got != expected {
198+
t.Errorf("Expected: %v got: %v\n", expected, got)
199+
}
200+
201+
})
202+
t.Run("Peak", func(t *testing.T) {
203+
queue, _ := NewCircularQueue[rune](10)
204+
205+
if err := queue.Enqueue('a'); err != nil {
206+
t.Error(err)
207+
}
208+
if err := queue.Enqueue('b'); err != nil {
209+
t.Error(err)
210+
}
211+
if err := queue.Enqueue('c'); err != nil {
212+
t.Error(err)
213+
}
214+
215+
expected := 'a'
216+
got, err := queue.Peek()
217+
if err != nil {
218+
t.Error(err.Error())
219+
}
220+
221+
if got != expected {
222+
t.Errorf("Expected: %v got: %v\n", expected, got)
223+
}
224+
})
225+
}
226+
227+
// BenchmarkCircularQueue benchmarks the CircularQueue implementation.
228+
func BenchmarkCircularQueue(b *testing.B) {
229+
b.Run("Enqueue", func(b *testing.B) {
230+
queue, _ := NewCircularQueue[int](1000)
231+
for i := 0; i < b.N; i++ {
232+
if err := queue.Enqueue(i); err != nil {
233+
b.Error(err)
234+
}
235+
}
236+
})
237+
238+
b.Run("Dequeue", func(b *testing.B) {
239+
queue, _ := NewCircularQueue[int](1000)
240+
for i := 0; i < 1000; i++ {
241+
if err := queue.Enqueue(i); err != nil {
242+
b.Error(err)
243+
}
244+
}
245+
b.ResetTimer()
246+
for i := 0; i < b.N; i++ {
247+
if _, err := queue.Dequeue(); err != nil {
248+
b.Error(err)
249+
}
250+
}
251+
})
252+
253+
b.Run("Peek", func(b *testing.B) {
254+
queue, _ := NewCircularQueue[int](1000)
255+
for i := 0; i < 1000; i++ {
256+
if err := queue.Enqueue(i); err != nil {
257+
b.Error(err)
258+
}
259+
}
260+
b.ResetTimer()
261+
for i := 0; i < b.N; i++ {
262+
if _, err := queue.Peek(); err != nil {
263+
b.Error(err)
264+
}
265+
}
266+
})
267+
}
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)