Coding
Data Structures

Ring Buffer From Scratch

Difficulty

Asked at Citadel Securities, Jump Trading

Theory: Data Structures You Implement, Not Import

Read the problem, hints and solution here. The editor needs a bigger screen: open this page on a laptop to write and run your code.

Available in C++ and Python. You are solving it in C++. Switch to Python

Every low-latency system has a ring buffer somewhere in its hot path: market-data handlers stage packets in one, loggers hand records across threads through one, and exchange gateways queue outbound orders in one. The reason is mechanical sympathy: a fixed block of memory, no allocation after construction, and index arithmetic instead of pointer chasing.

Implement RingBuffer<T>, a fixed-capacity FIFO over a pre-allocated buffer:

  • explicit RingBuffer(std::size_t capacity): allocates storage for exactly capacity elements, once, here. capacity >= 1.
  • bool push(const T& value): appends at the tail; returns false (and changes nothing) when full.
  • std::optional<T> pop(): removes and returns the head; std::nullopt when empty.
  • std::optional<T> front() const: the head without removing it.
  • std::size_t size() const, bool empty() const, bool full() const.

No std::deque, std::queue, or std::list: the point is the index arithmetic over contiguous storage (a std::vector<T> sized once in the constructor is fine as the backing store). Indices must wrap; the buffer must keep working through many times capacity operations.

Examples

[]{ RingBuffer<int> rb(2); std::vector<int> out; out.push_back(rb.push(1)); out.push_back(rb.push(2)); out.push_back(rb.push(3)); return out; }()
// [1, 1, 0]  (third push rejected: full)

[]{ RingBuffer<int> rb(2); rb.push(7); rb.push(8); std::vector<int> out; out.push_back(*rb.pop()); rb.push(9); out.push_back(*rb.pop()); out.push_back(*rb.pop()); return out; }()
// [7, 8, 9]  (wrap-around preserves FIFO order)

Constraints

  • Element type is copyable; capacity up to \(10^6\).
  • All operations must be \(O(1)\): the hidden test drives tens of millions of operations through a small buffer, so per-operation shifting or reallocation will not finish in time.
  • Track size explicitly or reserve a slot; both are acceptable, but full() and empty() must be exact (the classic head==tail ambiguity is yours to resolve).

In Python

The same class, RingBuffer(capacity), with push(value) returning False when full, pop() and front() returning None when empty, and size(), empty(), full(). Allocate one list in the constructor and never call list.pop(0) or insert(0, ...), which shift every element and are the \(O(n)\) this structure exists to avoid. collections.deque is banned. The provided driver run_ring_buffer(capacity, ops) applies a list of operations to a fresh buffer and is what the tests call.

Rate this problem
Language:RingBuffer
Rate this problem