Linear Data Structures — Stack & Queue
Stacks and queues are two of the most fundamental data structures in computer science. A stack is LIFO (Last-In, First-Out), a queue is FIFO (First-In, First-Out). Together they underlie undo systems, browser history, breadth-first search, message brokers, and CPU schedulers. Let's see how to build them with Python's lists, collections.deque, and queue.Queue.
Overview
LIFO / FIFO
Two opposite ordering rules — both fundamental in CS.
O(1) Ops
Push and pop are constant-time when using the right structure.
Multiple Tools
Lists, deques, and Queue cover every scenario.
Thread Safe
queue.Queue for safe multi-threaded producers/consumers.
Core for Algorithms
DFS uses stacks; BFS uses queues — they enable graph traversal.
Syntax
- Stack via list:
append()to push,pop()to remove last. - Queue via
collections.deque:append()+popleft()for O(1) FIFO. - Avoid
list.pop(0)for queues — it is O(n). - For multi-threaded queues use
queue.Queue.
# Stack with list
stack = []
stack.append(1); stack.append(2); stack.append(3)
print(stack.pop()) # 3 (LIFO)
# Queue with deque
from collections import deque
q = deque()
q.append("a"); q.append("b"); q.append("c")
print(q.popleft()) # 'a' (FIFO)
Detailed Explanation
- Stack — LIFO: The newest item is removed first. Think of a stack of plates: you take the top one off.
- Queue — FIFO: The oldest item is removed first. Think of a queue at the bank: first come, first served.
- List-based stack: Python lists already support push (
append) and pop (pop()) in O(1). Lists make excellent stacks. - List-based queue (don't!):
list.pop(0)is O(n) because every other element shifts. Usecollections.dequeinstead — it does O(1) at both ends. - Using deque:
dequeis a double-ended queue. Useappend+popleftfor FIFO, orappendleft+popfor reverse-FIFO. - Thread-safe queue: For producer-consumer patterns across threads, use
queue.Queue— it handles synchronisation automatically.
Code Examples
stack = []
for x in [1, 2, 3, 4]:
stack.append(x)
while stack:
print(stack.pop(), end=" ")
from collections import deque
q = deque()
for x in ["a", "b", "c", "d"]:
q.append(x)
while q:
print(q.popleft(), end=" ")
def balanced(s):
pairs = {")":"(","]":"[","}":"{"}
stack = []
for ch in s:
if ch in "([{":
stack.append(ch)
elif ch in ")]}":
if not stack or stack.pop() != pairs[ch]:
return False
return not stack
print(balanced("([]{})"))
print(balanced("([)]"))
False
def reverse(s):
stack = list(s)
return "".join(stack.pop() for _ in range(len(s)))
print(reverse("Python"))
from queue import Queue
q = Queue()
for i in range(5):
q.put(i)
while not q.empty():
print(q.get(), end=" ")
from collections import deque
graph = {1:[2,3], 2:[4], 3:[5], 4:[], 5:[]}
seen, order = set(), []
q = deque([1])
while q:
n = q.popleft()
if n in seen: continue
seen.add(n); order.append(n)
q.extend(graph[n])
print(order)
Real-World Use Cases
Undo / Redo
Editor undo systems push every change on a stack.
Browser History
Back/Forward navigation uses two stacks.
Message Queues
RabbitMQ / Kafka deliver messages in FIFO order.
CPU Scheduling
Operating systems queue ready processes for execution.
Graph Algorithms
DFS uses a stack, BFS uses a queue.
Print Spooler
Print jobs are processed in FIFO order from a queue.
Notes & Pro Tips
- Use
listfor stacks —append/popare both O(1). - Use
collections.dequefor queues — both ends are O(1). - Never use
list.pop(0)for queues — it is O(n). - Use
queue.Queueonly when threads are involved; in single-threaded code it's overkill. - Stacks and queues are abstractions — the API matters, not the underlying container.
collections.deque(maxlen=N)creates a fixed-size circular buffer.
Common Mistakes
- Using list as queue:
pop(0)shifts every element — terrible performance. - Empty pop:
list.pop()on an empty list raisesIndexError. - Wrong end: using
append+popon deque gives LIFO, not FIFO. - Mixing semantics: calling a stack a 'queue' confuses readers — name variables correctly.
- Forgetting thread-safety: deque is not safe across threads;
queue.Queueis. - Recursion limit: deep DFS via recursion may hit Python's recursion limit — use an explicit stack.
Practice Problems
- Problem 1: Implement a stack class with push, pop, peek, and is_empty methods.
- Problem 2: Implement a queue class using
collections.deque. - Problem 3: Use a stack to evaluate a postfix expression.
- Problem 4: Use a queue to print numbers from 1 to N in level-order from a binary tree.
- Problem 5: Check if a string is a palindrome using one stack and one queue.
- Problem 6: Implement a circular queue of fixed capacity 5.
Interview Questions
- Q1. What is the difference between a stack and a queue?
- Q2. Why is
list.pop(0)slow? - Q3. When would you use
collections.dequeover a list? - Q4. What is the difference between
queue.Queueandcollections.deque? - Q5. How would you implement a queue using two stacks?
- Q6. Explain the use of stacks in expression evaluation.
Frequently Asked Questions
- Q1: What's the difference between a stack and a queue?
A stack is LIFO — last in, first out. A queue is FIFO — first in, first out. - Q2: How do I build a stack in Python?
Use a list: append to push, pop() to remove. Both are O(1). - Q3: How do I build a queue in Python?
Use collections.deque with append() and popleft(). Both ends are O(1). - Q4: When should I use queue.Queue?
When multiple threads must put and get safely. Otherwise deque is faster. - Q5: Are stacks recursive?
Recursion itself uses the call stack. Many recursive algorithms can be rewritten iteratively using an explicit stack. - Q6: What is a priority queue?
A queue where items are dequeued by priority. Use the heapq module for an efficient implementation.
Summary
Stacks and queues are simple, powerful abstractions that show up everywhere — from undo systems to graph algorithms. In Python, lists give you fast stacks, collections.deque gives you fast queues, and queue.Queue covers thread-safe scenarios. Match the structure to the access pattern and your code will be both elegant and efficient.
Continue Learning
Previous
Go to Previous Chapter