skip to content

Stacks & Queues

Stacks and queues are the workhorse LIFO and FIFO abstractions behind call stacks, BFS/DFS, buffers, and schedulers. Interviewers use them to test whether you can match an access discipline to a problem and reason about O(1) operation guarantees.

part ofData structures & algorithmsoverview, primer and where to startread it →
on this pageshow

questions

page 1 of 2

How does a deque give you both stack and queue behavior, and what does each of its four operations cost?

level: juniorimportance: must knowfreq 62%

answer

  1. Count the ends, then count the operations
  2. Same end twice, or opposite ends
  3. One end gives LIFO, two ends give FIFO
  4. What a plain array charges to insert at the front
  5. Amortized versus worst case per push

basics

~20 s

A deque supports push and pop at both ends, each constant time. Pushing and popping at the same end gives LIFO stack behavior; pushing at one end and popping at the other gives FIFO queue behavior.

solid answer

~40 s

A deque (double-ended queue) exposes four mutating operations — push front, push back, pop front, pop back — and all four are constant time. Restrict yourself to one end for both pushing and popping and you have a stack: the last item in is the first out. Push at one end and pop at the other and you have a queue: items leave in arrival order. That is the whole trick, and it is why a deque can replace both. The cost claim needs care, though: in a single-array backing that grows by doubling, the four operations are O(1) *amortized* — one unlucky push copies the whole buffer — while node- or block-based backings give O(1) *worst case* per operation. Neither promises indexed access to the middle; only array-backed deques offer that.

go deeper

for a junior

Be ready to name the four operations and say, without hesitating, which pair gives LIFO and which pair gives FIFO. Knowing that both ends are constant time is the point of the structure.

for a middle

Explain why a plain dynamic array is O(n) at the front and what a deque does differently — a floating head offset or a pointer at each end — and distinguish amortized from worst-case constant time.

for a senior

Show that you treat a deque as an interface with several backings: say which operations your workload actually issues, and whether you need indexed access or a per-operation latency bound before you pick one.

for a principal

Own the API question. Decide whether a shared codebase gets the full four-operation surface or narrow stack and queue facades over it, and be able to justify that call in terms of the ordering bugs the wider surface makes possible.

## What a deque is A **deque** (double-ended queue, pronounced "deck") is a linear sequence whose defining property is not what it stores but what it *costs*: you may add and remove at **either** end in constant time. The core surface is four mutating operations — push at the front, push at the back, pop from the front, pop from the back — usually joined by non-destructive peeks at each end and a size query. What makes that contract notable is that the obvious sequence structures do **not** offer it: | Structure | push back | pop back | push front | pop front | |---|---|---|---|---| | Growth-doubling dynamic array | O(1) amortized | O(1) | **O(n)** | **O(n)** | | Singly linked list (head pointer only) | **O(n)** | **O(n)** | O(1) | O(1) | | Deque | O(1) | O(1) | O(1) | O(1) | A dynamic array is slow at the front because element 0 is pinned to slot 0: inserting or removing there shifts every other element one position. A deque removes that pin. An array-backed deque stores a *head offset* and lets the occupied region float (and wrap) inside the buffer, so there is spare room at both ends; a node-based deque simply keeps a pointer to each end and links nodes in both directions. Either way, both ends are cheap. ## Recovering a stack and a queue Because a deque is unrestricted, you get the two classic disciplines by *choosing which pair of operations you use*: - **Stack (LIFO)** — push and pop at the **same** end. Push back / pop back and push front / pop front are equally valid; the items come out newest-first either way. This is what a call-frame stack or a backtracking search wants. - **Queue (FIFO)** — push at **one** end and pop at the **other**. Push back / pop front is the conventional choice, and push front / pop back is its mirror. Items leave in arrival order, which is what a breadth-first frontier or a work backlog wants. So a deque is a strict superset of both abstractions, at the same asymptotic cost. That is why it is the default recommendation when you need one of them and have no reason to prefer a specialized type. ## What "O(1)" does and does not promise here This is where the question is actually won or lost in an interview. - In a **single growable array** backing, pushes are **amortized** O(1): most cost a couple of instructions, but the push that overflows the buffer allocates a bigger one and copies every element, costing O(n). Averaged over a worst-case *sequence* of n pushes the total is O(n), hence O(1) each — but no individual push is guaranteed fast. Amortized is a statement about a sequence, not about a distribution of inputs and not about one call. - In a **node-based or fixed-block** backing, each operation touches a bounded amount of memory, so the bound is O(1) worst case per operation (block-based designs still occasionally grow a small index table). - Pops are O(1) in every reasonable backing; array-backed deques usually clear the vacated slot so the removed element is not kept reachable. ## The two things people wrongly assume **"A deque is indexable."** Only sometimes. An array-backed deque can compute the slot for logical position i from the head offset in constant time, so indexed reads are O(1). A node-based deque has to walk, so position i costs O(i). If your algorithm indexes into the middle, the backing you get matters and you should not assume. **"Insertion in the middle is cheap too."** It is not part of the contract. Middle insertion is O(n) in an array backing (shifting) and O(n) in a node backing (finding the node), even though relinking, once you already hold the node, is O(1). ## The API-surface caution A deque's flexibility has a real cost on a team: a type that offers all four operations no longer documents the ordering discipline the code depends on. If a background worker drains a shared backlog and someone "fixes" a line to pop from the back, the backlog silently turns from FIFO into LIFO — no crash, no type error, just old items starving under load. That is why many codebases keep the deque as the *implementation* and expose narrow stack-shaped or queue-shaped wrappers as the *interface*, so the restriction is enforced by the type rather than by reviewer attention. ## How to answer out loud Name the four operations, state the constant-time contract, give the two restrictions that recover LIFO and FIFO, and then volunteer the amortized-versus-worst-case distinction before you are asked. Adding "and indexing is only O(1) if it is array-backed" signals that you know a deque is an interface with several implementations, not one structure.

  • If a deque already does everything a stack and a queue do, why would a team still expose narrower stack and queue types?
    Because the narrow type encodes the invariant the code depends on. With all four operations reachable, any caller can pop from the wrong end and silently flip a backlog from FIFO to LIFO — no error, just starvation of the oldest work under load. A stack-shaped or queue-shaped wrapper over the same deque makes the discipline checkable at compile time instead of at review time, and it documents intent for the next reader.
  • Does the O(1) claim mean every individual push is fast?
    No. In a single-array backing it is amortized: the push that fills the buffer allocates a larger one and copies every element, so that one call is O(n) while the sequence of n pushes totals O(n). Node- and block-based backings give a genuine per-operation worst-case bound instead, which matters when a latency budget cares about the tail rather than the mean.
  • Can you index into the middle of a deque in constant time?
    Only if it is array-backed. There, logical position i maps to a slot by offsetting from the stored head and wrapping, so the read is O(1). A doubly linked node backing must walk from an end, making position i cost O(i). The deque contract itself only promises the two ends, so an algorithm that indexes the middle should pick the backing deliberately.

A deque is a train platform with a door at both ends: board and leave through one door and the last aboard is first off; board at one door and leave by the other and everyone exits in arrival order.

saying these in an interview costs you the question

  • Calls a deque just a queue you can also peek at from the back
  • Says pushing at the front must be O(n) like array insertion
  • Claims O(1) means every single operation is uniformly fast
  • Thinks recovering a stack needs both ends of the deque
  • Assumes every deque supports constant-time indexing

context

open as a page

Array-backed vs linked-node stack: what does a single push cost in each?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Linked-node push is O(1) worst case: allocate a node and link it to the head. Array-backed push is O(1) amortized: usually one slot write, but a full buffer forces copying every element into a larger block.

open as a page

In a queue built from two stacks, when may elements move from the inbound to the outbound stack?

level: juniorimportance: must knowfreq 74%

basics

~20 s

Only when the outbound stack is empty. Pushing the inbound stack's contents onto a non-empty outbound stack puts newer arrivals above older ones and destroys first-in-first-out order. Standard implementations drain the inbound stack completely in that one transfer.

open as a page

Why does level-order traversal of a company org chart use a queue instead of a stack?

level: juniorimportance: must knowfreq 78%

basics

~20 s

A queue hands nodes back in the order they were discovered, so every person at one depth is expanded before anyone a depth lower. A stack returns the most recent discovery first, which dives down a single reporting chain instead.

open as a page

Why is dequeuing from the front of an array-backed queue O(n), and what does that cost a backlog?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Removing the element at index 0 slides every remaining element one slot toward the front, so a single dequeue costs O(n). Draining a backlog of n items that way costs about n^2/2 moves in total.

open as a page

Why does dequeuing from a ring buffer cost O(1) when a shift-down array queue costs O(n)?

level: juniorimportance: must knowfreq 72%

basics

~20 s

A ring buffer moves an index instead of data: dequeue reads the slot at head, then advances head by one, wrapping to zero past the last slot. Nothing is copied. A shift-down queue relocates every remaining element.

open as a page

Why does validating nested delimiters of several types need a stack rather than a counter?

level: juniorimportance: must knowfreq 80%

basics

~20 s

A counter records how many delimiters are open, not which ones. With several delimiter types you must know the type of the most recently opened one to reject overlapping pairs, and last-opened-first-closed is exactly what a stack gives you.

open as a page

One dequeue in a two-stack queue touches every element, so why is dequeue still amortized O(1)?

level: middleimportance: must knowfreq 62%

basics

~20 s

Each element is pushed at most twice and popped at most twice across its entire lifetime, so any sequence of m operations does O(m) work in total. The one expensive transfer pays for all the cheap removals that follow it.

open as a page

Why does a minimum-tracking stack break when its auxiliary stack records only strictly smaller values?

level: middleimportance: must knowfreq 66%

basics

~20 s

Two equal minima produce only one auxiliary entry, so removing the first occurrence discards the record while an equally small value is still stored. The structure then reports a minimum that is too large. Record on less-than-or-equal, or store counts.

open as a page

In level-order traversal, why snapshot the queue's size before processing each level?

level: middleimportance: must knowfreq 66%

basics

~20 s

The snapshot freezes how many nodes belong to the current level before any of their children are added. Without it the loop bound keeps growing as children are inserted, and the level boundary is lost.

open as a page

In a circular-array queue, how do wrapping head and tail indices make dequeue O(1)?

level: middleimportance: must knowfreq 62%

basics

~20 s

A circular-array queue never moves stored elements. Dequeue advances the front index by one modulo the array's capacity, and enqueue writes at the wrapped back position, so both are O(1) and freed front slots get reused.

open as a page

Why is head == tail ambiguous in a ring buffer with no count field, and how do you fix it?

level: middleimportance: must knowfreq 62%

basics

~20 s

With two indices and all N slots usable, head == tail means empty after zero writes and full after N writes — indistinguishable. Fix it with a count field, one permanently unused slot, or non-wrapping counters.

open as a page

Why can a linked-node stack hold millions of elements when a thread's call stack overflows far sooner?

level: middleimportance: must knowfreq 62%

basics

~20 s

They are different things. A stack data structure is a LIFO discipline over memory you allocate on demand, so its ceiling is available memory. A thread's call stack is one fixed contiguous region reserved when the thread starts.

open as a page

An editor caps its undo history at 100 edits — why is a plain stack the wrong structure and a deque right?

level: middleimportance: should knowfreq 36%

basics

~20 s

Undo reads the newest entry, but a cap must discard the oldest — and those live at opposite ends. A stack only reaches one end, so trimming the oldest costs O(n); a deque appends at the back and evicts from the front, both constant time.

open as a page

In a deque-based symmetry check that pops both ends, why must the loop stop with one element left?

level: middleimportance: should knowfreq 42%

basics

~20 s

Because an odd-length input leaves a lone middle element with no partner. Looping while more than one element remains stops before that element, so the front pop never empties the deque and leaves the back pop with nothing to remove.

open as a page

For a stack holding a billion small values, what does linked backing cost that array backing does not?

level: middleimportance: should knowfreq 44%

basics

~20 s

Every linked element carries a next-pointer plus allocator bookkeeping, so a small payload often costs two to three times its own size. Array backing instead pays in unused reserved slots and needs one enormous contiguous block.

open as a page

What goes wrong if an array-backed stack grows when full and shrinks at half capacity?

level: middleimportance: should knowfreq 36%

basics

~20 s

Growing at full and shrinking at half leaves no gap between the triggers, so a stack at the boundary oscillates: push grows and copies, pop shrinks and copies back. Every operation becomes O(n). Shrink at one-quarter instead.

open as a page

In a linked-node queue, what breaks in the tail reference when the last element is dequeued?

level: middleimportance: should knowfreq 50%

basics

~20 s

Dequeuing the last element empties the head reference but leaves the tail pointing at the node just removed. A later enqueue then links onto that detached node while head stays empty, so the item is lost and the queue looks permanently empty.

open as a page

Why size a ring buffer to a power of two, and when is the index-masking trick not worth it?

level: middleimportance: should knowfreq 38%

basics

~10 s

A power-of-two capacity makes index mod N exactly index & (N-1): a bitwise mask instead of a division, which matters in tight per-item loops. It stops paying when rounding capacity up wastes real memory.

open as a page

In iterative depth-first traversal with an explicit stack, why push children in reverse order?

level: middleimportance: should knowfreq 40%

basics

~20 s

A stack returns the most recent push, so pushing children left to right makes the rightmost child come out first. Pushing them right to left puts the leftmost on top, which reproduces the order recursion visits siblings in.

open as a page

In a stack-based postfix evaluator, why must the first value popped be the right operand?

level: middleimportance: should knowfreq 52%

basics

~20 s

Operands are pushed left to right, so the top of the stack is the right-hand operand and the value beneath it is the left. Reversing the two pops silently inverts subtraction and division while sums and products still look correct.

open as a page

In an array-backed stack, what changes in push, pop and the empty check if `top` means the next free slot?

level: middleimportance: should knowfreq 52%

basics

~20 s

Two conventions exist: top indexes the last element (empty is top == -1, push increments then writes), or top indexes the next free slot (empty is top == 0, push writes then increments). Pick one and check every operation against it.

open as a page

Why do most production deques use a circular or chunked array rather than a doubly linked list?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Both backings hit O(1) at each end, so the decision is constants: a linked list pays two pointers and an allocation per element and defeats cache prefetching, while array-backed storage keeps elements contiguous. The linked form wins mainly when per-operation worst-case latency matters more than throughput.

open as a page

A matchmaking lobby queue misses its p99 budget on rare enqueues — how do you fix the backing?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Amortized O(1) enqueue hides one operation that copies the whole buffer, landing on one unlucky player at the tail. Reserve the peak capacity up front, or use a backing with no bulk copy, then verify against the tail percentile.

open as a page

Building a stack from two queues, which of push or pop do you make O(n), and why must one of them be?

level: seniorimportance: should knowfreq 40%

basics

~20 s

One must be linear because a queue only removes from the front while the newest element sits at the back. Make push linear when reads and removals dominate; make removal linear when insertions dominate. Nothing here amortizes.

open as a page

A batch transcoder's fetch stage fills its job queue faster than the encode stage drains it — what fails first?

level: seniorimportance: should knowfreq 46%

basics

~10 s

Memory fails first: an unbounded queue turns a rate mismatch into unbounded growth, so the backlog itself becomes the leak. Queueing delay grows with it, and a crash discards every job still resident.

open as a page

When should a ring buffer overwrite its oldest record instead of rejecting the newest write?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Overwrite when recency beats completeness — a crash-diagnosis recorder wants the last N events whatever came before. Reject when every record must survive, such as billed usage or an audit trail. Either way, count the losses and expose them.

open as a page

In a two-stack undo/redo design, why must a new user action clear the redo stack?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Redo entries are only meaningful against the exact state they were undone from. A new action rewrites history from that point, so replaying them would target objects that no longer exist. Clearing the redo stack keeps the model honest.

open as a page

What should pop on an empty stack do — signal an error, or return a sentinel value like -1?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Signal the failure out of band. A sentinel is safe only when it lies outside the element domain, and for signed sensor readings -1 is a legal reading, so the caller cannot tell emptiness from data and the corruption is silent.

open as a page

showing 1–30 of 34