Stacks and Queues in Python: Balanced Brackets, and deque vs list as a Queue
A stack pops from the same end it pushes to (LIFO); a queue pops from the opposite end it pushes to (FIFO). This post uses a plain Python list as a stack to check bracket balance, collections.deque as a FIFO queue, then times popping from the front of a deque against popping from the front of a list.
The code
import time
from collections import deque
def is_balanced(expr):
# Stack via a plain list: push on open bracket, pop on close bracket.
# If the popped bracket doesn't match, or the stack is empty at a
# close bracket, or non-empty at the end, the expression is unbalanced.
pairs = {")": "(", "]": "[", "}": "{"}
stack = []
for ch in expr:
if ch in "([{":
stack.append(ch)
elif ch in ")]}":
if not stack or stack.pop() != pairs[ch]:
return False
return len(stack) == 0
tests = ["(a+b)*[c-d]", "([)]", "{[()]}", "(("]
print("=== Stack: balanced brackets ===")
for expr in tests:
print(f"{expr!r:>14} -> {is_balanced(expr)}")
print("\n=== Queue: deque as a FIFO ===")
q = deque()
q.append("a")
q.append("b")
q.append("c")
print("after append a, b, c:", list(q))
print("popleft():", q.popleft())
print("queue now:", list(q))
# Time popping from the front: deque.popleft() vs list.pop(0)
n = 20000
print(f"\ntiming {n} front-pops: deque.popleft() vs list.pop(0)")
dq = deque(range(n))
start = time.perf_counter()
while dq:
dq.popleft()
deque_time = time.perf_counter() - start
lst = list(range(n))
start = time.perf_counter()
while lst:
lst.pop(0)
list_time = time.perf_counter() - start
print(f"deque.popleft(): {deque_time:.6f}s")
print(f"list.pop(0): {list_time:.6f}s")
is_balanced pushes each open bracket onto stack and pops on a close bracket, checking the popped value against the expected match. The queue section appends 3 items to a deque then popleft()s one to show FIFO order, then times draining an n = 20000 element deque with popleft() against draining an equal-size list with pop(0).
Running it
Real output:
=== Stack: balanced brackets ===
'(a+b)*[c-d]' -> True
'([)]' -> False
'{[()]}' -> True
'((' -> False
=== Queue: deque as a FIFO ===
after append a, b, c: ['a', 'b', 'c']
popleft(): a
queue now: ['b', 'c']
timing 20000 front-pops: deque.popleft() vs list.pop(0)
deque.popleft(): 0.000781s
list.pop(0): 0.016423s
The bracket checker got all four cases right against what each expression actually looks like: '(a+b)*[c-d]' and '{[()]}' balanced, '([)]' (crossed brackets) and '((' (unclosed) did not. On the queue, popleft() returned 'a', the first item appended, leaving ['b', 'c'] — FIFO order. In the timing run, draining 20000 items with deque.popleft() took 0.000781s versus 0.016423s for list.pop(0) on the same size — about 21x longer for list.pop(0) in this run.
Takeaway
The run showed a working stack (list) correctly balancing brackets including the crossed-bracket and unclosed-bracket cases, a working FIFO queue (deque) returning items in insertion order, and deque.popleft() measuring about 21x faster than list.pop(0) for 20000 front-removals.