Unit 1 · Algorithms · Lesson 5 of 24
Stacks, queues and deques
Builds on
Step 1: Learn the ideas
- Stack (LIFO) and queue (FIFO) interfaces
- Balanced delimiters and nested structure
- Expression evaluation: postfix, precedence and parentheses
- Augmented stacks (min stack)
- Queue from two stacks and amortized analysis
- Ring buffers: modular indices, full versus empty
- Deques and their library implementations
- Explicit stacks in place of recursion
- Queues for BFS and time-window buffering
Why it matters
Many problems hide a rule about which item to handle next. If the most recently opened thing must be closed first (brackets, function calls, undo history, nested encodings), the structure is a stack. If things must be handled in arrival order (BFS levels, request buffers, events inside a time window), it is a queue. A deque supports both ends and underlies the monotonic-deque technique. In systems the same structures appear as call stacks, ring buffers in drivers and logging pipelines, and work queues.
Core ideas
Pattern: Stack. Push when something opens, pop when it closes, and check that what you pop matches. For Valid Parentheses the invariant is "the stack holds exactly the unmatched openers, innermost on top"; the string is valid when every closer matches the top and the stack ends empty. The same shape handles postfix evaluation (push operands; on an operator pop two, minding operand order for subtraction and division), Decode String (push the current context when entering brackets, pop and combine when leaving) and simulations such as Asteroid Collision. In all of them each element is pushed and popped at most once, so a while loop inside the for loop still gives O(n) total.
Augmented stacks. Store extra state with each entry. A min stack pushes (value, min_so_far): the minimum of a stack changes only on push or pop, so the top entry always knows it.
Queues and amortization. A queue built from two stacks pushes onto inbox and pops from outbox, refilling outbox from inbox only when outbox is empty. One pop can cost O(n), but each element moves from inbox to outbox at most once, so any n operations cost O(n): amortized O(1) per operation. Be able to state this argument in two sentences.
Ring buffers. A fixed array with a head index and a count implements a bounded queue or deque: enqueue at (head + count) % cap, dequeue at head and advance it modulo cap; a front insertion moves head back by one modulo cap. Keeping count (or leaving one slot unused) removes the classic ambiguity where head == tail could mean either full or empty.
Deques. O(1) push and pop at both ends. Use them for BFS queues, for windows that evict old items, and as the basis of the monotonic-deque pattern. Python has collections.deque, Java ArrayDeque, C++ std::deque; in JavaScript use an array with a head index or write a small ring buffer.
Explicit stacks for recursion. Any recursive DFS can be written with a stack of pending work. This avoids recursion limits (CPython's default is typically 1000 frames) and makes the order explicit. Push children in reverse order to match the recursive pre-order; for post-order, push a node twice with a flag that says whether its children are done.
Common misconceptions
- "A dynamic array works as a queue." Removing from the front shifts every remaining element (Python
list.pop(0); JavaScriptshift()is specified the same way), which can make a BFS quadratic. - "A queue from two stacks is O(n) per operation." A single operation can be O(n), but the amortized cost is O(1) because each element moves once.
- "
head == tailmeans empty." Without a count or a spare slot it can also mean full; choose one convention and test both states and wrap-around. - "A loop inside a loop over a stack is quadratic." If every element is pushed and popped at most once, the total work is linear.
- "An iterative DFS visits nodes in the same order as the recursive one." Only if you push children in reverse order.
How to approach it
When you see nesting, matching or "most recent", reach for a stack and say what it holds. When you see levels, arrival order or a time window, reach for a queue or deque. Count pushes and pops to argue linear time. Interviewers typically extend these problems: add a getMin, bound the capacity, support both ends, or make the queue safe for concurrent producers and consumers, a natural follow-up for backend and systems roles.
Suggested order
- 1Read MIT 6.006 Lecture 2 (Data Structures) for sequence operations at either end and the amortized growth of array-backed stacks.
- 2Read the note on using lists as queues in the Python tutorial, then find the equivalent deque type and its costs in your own language.
- 3Solve Valid Parentheses and Evaluate Reverse Polish Notation, stating the stack invariant before coding.
- 4Solve Min Stack and Implement Queue using Stacks, writing the amortized argument for the latter in two sentences.
- 5Solve Number of Recent Calls with a queue, then Design Circular Queue with an explicit full and empty convention.
- 6Solve Decode String and Asteroid Collision, then Design Circular Deque.
- 7Solve Basic Calculator as the harder review problem, saving context on a stack at each parenthesis.
Step 2: Check off what you can do
Tap each one you can genuinely do or explain. Each earns 10 XP.
Step 3: Go deeper
MIT 6.006 Introduction to Algorithms (Spring 2020)Start here
MIT OpenCourseWareSection: Lecture 2: Data Structures
The sequence interface, operations at the first and last positions, and amortized dynamic-array growth behind array-backed stacks.
Open(opens in a new tab)Link responded, 5 Oct 2026Python documentation: costs of built-in types and the container modules
Python Software Foundation (docs.python.org)Section: Tutorial 5.1.2: Using Lists as Queues
The documented reason lists are poor queues and the deque alternative; check the equivalent guidance for your language.
Open(opens in a new tab)Link responded, 5 Oct 2026cppreference: C++ containers library, priority_queue, sort and lower_bound
cppreference.comSection: std::deque
Constant-time operations at both ends, and which deque operations invalidate iterators versus references.
Open(opens in a new tab)Link responded, 5 Oct 2026Java SE 25 API documentation: java.util collections
OracleSection: ArrayDeque
Why ArrayDeque is the documented choice for both stacks and queues in Java, and which of its operations are linear.
Open(opens in a new tab)Link responded, 5 Oct 2026
Step 4: Practice
For each problem, say what the stack or queue holds and when an element leaves it, then count pushes and pops to justify linear time. Test the design problems (Design Circular Queue, Design Circular Deque) explicitly in the empty, full and wrapped-around states. Basic Calculator is the harder review problem: a stack of saved contexts plus careful sign handling.
- Valid ParenthesesLeetCode(opens in a new tab)Not attemptedLeetCode
- Min StackLeetCode(opens in a new tab)Not attemptedLeetCode
- Evaluate Reverse Polish NotationLeetCode(opens in a new tab)Not attemptedLeetCode
- Implement Queue using StacksLeetCode(opens in a new tab)Not attemptedLeetCode
- Number of Recent CallsLeetCode(opens in a new tab)Not attemptedLeetCode
- Design Circular QueueLeetCode(opens in a new tab)Not attemptedLeetCode
- Decode StringLeetCode(opens in a new tab)Not attemptedLeetCode
- Asteroid CollisionLeetCode(opens in a new tab)Not attemptedLeetCode
- Design Circular DequeLeetCode(opens in a new tab)Not attemptedLeetCode
- Basic CalculatorLeetCode(opens in a new tab)Not attemptedLeetCode
- Design Browser HistoryLeetCode(opens in a new tab)Not attemptedLeetCode
Record attempts on each problem’s page (+15 XP, +10 more when solved on your own).
Step 5: Test yourself
Answer each one out loud or on paper before you finish the lesson.
- Question 1What does the stack contain at each step of Valid Parentheses?
- Question 2Why is a queue from two stacks amortized O(1) even though one pop can move n elements?
- Question 3How do you tell full from empty in a ring buffer?
- Question 4Which type do you use for a FIFO queue in your interview language, and what does removing from the front cost?
- Question 5How do you obtain pre-order versus post-order with an explicit stack?
You’re done when you can
- Solve two unseen stack problems in under 20 minutes each, stating the invariant before coding.
- Explain the amortized cost of a queue built from two stacks without notes.
- Implement a ring-buffer deque that passes empty, full and wrap-around tests on the first run.
- Rewrite a recursive tree traversal iteratively with an explicit stack, in pre-order and post-order.
Finish the lesson
Unlocks next: Monotonic stacks and deques, Recursion and backtracking, Graph traversal: BFS, DFS and components


