data-structures · intermediate · ~30 min
Amortised O(1) queue from two LIFO stacks — and ownership of the drain trick.
Build a FIFO queue whose internals are two LIFO stacks.
Implement a fixed-capacity int queue backed internally by two stacks (in_stack and out_stack):
typedef struct ts_queue ts_queue_t;
ts_queue_t *tsq_create(int capacity);
int tsq_enqueue(ts_queue_t *q, int v); /* 0 ok, -1 full */
int tsq_dequeue(ts_queue_t *q, int *out); /* 0 ok, -1 empty */
int tsq_size(ts_queue_t *q);
void tsq_destroy(ts_queue_t *q);
Enqueue pushes onto in_stack. Dequeue pops from out_stack; if out_stack is empty, first drain all of in_stack into out_stack (which reverses the order so the oldest item ends up on top), then pop. The two stacks share one capacity budget: the total stored items must never exceed capacity.
capacity: the maximum number of items held at once (capacity > 0).v: the value to enqueue.out: where tsq_dequeue writes the removed value.tsq_enqueue: 0 on success, -1 if the queue is full.tsq_dequeue: 0 on success (writing the oldest value through out), -1 if empty.tsq_size: the current number of items across both stacks.tsq_create(3)
enqueue 1, 2, 3 (queue full)
tsq_enqueue(4) -> -1
tsq_dequeue(&v) -> 0, v == 1 (drain triggered, oldest comes out first)
tsq_dequeue(&v) -> 0, v == 2
tsq_enqueue(5) -> 0
tsq_dequeue(&v) -> 0, v == 3
tsq_dequeue(&v) -> 0, v == 5
tsq_dequeue(&v) -> -1 (empty)
in_stack into out_stack when out_stack is empty, never on every dequeue.The two-stack queue is a classic interview puzzle — and also a real engineering trick. Building a queue out of two stacks gives O(1) amortised enqueue/dequeue without circular-buffer wraparound.
capacity > 0; v to enqueue; out receives the dequeued value.
enqueue/dequeue return 0 on success or -1 (full/empty); dequeue writes through out; size returns the total count.
O(1) amortised enqueue/dequeue. Both stacks share the capacity budget.
typedef struct ts_queue ts_queue_t;
ts_queue_t *tsq_create(int capacity);
int tsq_enqueue(ts_queue_t *q, int v);
int tsq_dequeue(ts_queue_t *q, int *out);
int tsq_size(ts_queue_t *q);
void tsq_destroy(ts_queue_t *q);
Draining in_stack to out_stack on every dequeue, not just when out_stack is empty — that breaks the amortisation. Forgetting to free both stack arrays in destroy.
Dequeue from empty; fill exactly to capacity; mix enqueues and dequeues.
O(1) amortised — every element is moved between stacks at most twice.
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.