Implement a Circular Buffer
Jane StreetSoftware Engineer · Technical ScreenLast updated
Implement a fixed-capacity circular buffer (ring buffer) supporting constant-time FIFO operations using wraparound indexing.
Problem Statement
A circular buffer (or ring buffer) is a fixed-size, first-in-first-out (FIFO) data structure that connects the end of the buffer back to the beginning. It is widely used in high-frequency trading and low-latency systems for buffering streaming market data and order messages without dynamic memory allocations.
Implement the CircularBuffer class with the following methods:
__init__(capacity: int): Initializes the buffer with a fixed maximum capacitycapacity.enqueue(val: int) -> bool: Insertsvalat the back of the buffer. ReturnsTrueif the item was added successfully, orFalseif the buffer is already full.dequeue() -> Optional[int]: Removes and returns the value at the front of the buffer. ReturnsNoneif the buffer is empty.peek() -> Optional[int]: Returns the value at the front of the buffer without removing it. ReturnsNoneif the buffer is empty.is_empty() -> bool: ReturnsTrueif the buffer contains no elements, otherwiseFalse.is_full() -> bool: ReturnsTrueif the buffer has reached its maximum capacity, otherwiseFalse.size() -> int: Returns the current number of elements in the buffer.
All operations must execute in O(1) time and must not reallocate or resize the underlying fixed storage.
Class Interface
from typing import Optional
class CircularBuffer:
def __init__(self, capacity: int):
pass
def enqueue(self, val: int) -> bool:
pass
def dequeue(self) -> Optional[int]:
pass
def peek(self) -> Optional[int]:
pass
def is_empty() -> bool:
pass
def is_full() -> bool:
pass
def size() -> int:
pass
Examples
buf = CircularBuffer(3)
buf.enqueue(1) # returns True
buf.enqueue(2) # returns True
buf.enqueue(3) # returns True
buf.enqueue(4) # returns False (buffer is full)
buf.peek() # returns 1
buf.dequeue() # returns 1
buf.enqueue(4) # returns True (wraps around)
buf.dequeue() # returns 2
buf.size() # returns 2
Constraints
1 <= capacity <= 10^5-10^9 <= val <= 10^9- At most
2 * 10^5total calls will be made to any combination of methods.
Edge Cases
- Calling
dequeueorpeekon an empty buffer. - Enqueueing when the buffer is completely full.
- Multiple consecutive wrap-around cycles where
tailwraps past index0.
Hints
Hint 1
You can allocate a fixed-length list of size `capacity` upfront to prevent dynamic reallocations.
Hint 2
Tracking an explicit `size` counter along with `head` and `tail` pointers simplifies distinguishing between a full buffer and an empty buffer.
Hint 3
Use the modulo operator `(index + 1) % capacity` to wrap pointer indices back to the start of the underlying array.
Follow-up questions
- How would you adapt this implementation for a single-producer, single-consumer (SPSC) lock-free concurrency model in C++ or Rust?
- If the requirement changed such that enqueueing into a full buffer overwrites the oldest element instead of rejecting it, what changes would you make?
- How can you optimize the modulo operation if capacity is guaranteed to be a power of two?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.