A circular array treats a fixed-size array as if it wraps around. Indices advance modulo the capacity, so the structure forms a logical ring.
You use it when you need constant-time insertions and removals at both ends within a bounded buffer.
Problem
Maintain a sequence of capacity that supports:
- insert at front and back
- remove from front and back
- wrap indices without shifting elements
Structure
A circular array maintains:
- an array of size
- a head index
- a tail index
Positions wrap using modulo arithmetic:
Algorithm
Insert at the back:
push_back(A, x):
if size == capacity:
error "full"
A[tail] = x
tail = (tail + 1) mod capacity
size += 1Remove from the front:
pop_front(A):
if size == 0:
error "empty"
value = A[head]
head = (head + 1) mod capacity
size -= 1
return valueExample
Let capacity be .
| step | operation | array | head | tail |
|---|---|---|---|---|
| 1 | push 8 | [8, -, -, -, -] | 0 | 1 |
| 2 | push 3 | [8, 3, -, -, -] | 0 | 2 |
| 3 | pop | [8, 3, -, -, -] | 1 | 2 |
| 4 | push 5 | [8, 3, 5, -, -] | 1 | 3 |
| 5 | push 1 | [8, 3, 5, 1, -] | 1 | 4 |
| 6 | push 9 | [8, 3, 5, 1, 9] | 1 | 0 |
The tail wraps from index back to .
Correctness
At all times:
- elements occupy positions from head to tail in cyclic order
- modulo arithmetic ensures indices remain within bounds
- size tracks the number of valid elements
Thus operations maintain correct ordering without shifting elements.
Complexity
| operation | time |
|---|---|
| push_back | |
| push_front | |
| pop_front | |
| pop_back |
Space usage:
When to Use
Circular arrays are appropriate when:
- a fixed-size buffer is sufficient
- constant-time queue or deque operations are required
- memory allocation must remain stable
They are less suitable when:
- capacity must grow dynamically
- random insertions in the middle are required
Implementation
class CircularArray:
def __init__(self, capacity):
self.a = [None] * capacity
self.capacity = capacity
self.head = 0
self.tail = 0
self.size = 0
def push_back(self, x):
if self.size == self.capacity:
raise Exception("full")
self.a[self.tail] = x
self.tail = (self.tail + 1) % self.capacity
self.size += 1
def pop_front(self):
if self.size == 0:
raise Exception("empty")
value = self.a[self.head]
self.head = (self.head + 1) % self.capacity
self.size -= 1
return valuetype CircularArray[T any] struct {
data []T
head int
tail int
size int
capacity int
}
func NewCircularArray[T any](capacity int) *CircularArray[T] {
return &CircularArray[T]{
data: make([]T, capacity),
capacity: capacity,
}
}
func (c *CircularArray[T]) PushBack(x T) bool {
if c.size == c.capacity {
return false
}
c.data[c.tail] = x
c.tail = (c.tail + 1) % c.capacity
c.size++
return true
}
func (c *CircularArray[T]) PopFront() (T, bool) {
var zero T
if c.size == 0 {
return zero, false
}
v := c.data[c.head]
c.head = (c.head + 1) % c.capacity
c.size--
return v, true
}