Algorithm Templates: Stack
The stack is one of the most versatile data structures in coding interviews. Whether you’re matching parentheses, evaluating expressions, or finding the next greater element in an array, a stack gives you an elegant O(n) solution. This guide collects the essential C++ templates you’ll need, organized by pattern so you can quickly find the right approach for any stack problem.
New to Stack problems? A stack is Last-In-First-Out (LIFO). The key insight: whenever a problem asks you to match, nest, or find the “next greater/smaller” element, think stack.
Contents
- Parentheses Matching
- Expression Evaluation
- Nested Structure Processing
- Monotonic Stack & Deque Patterns
- Pattern 1: Next Greater Element
- Pattern 2: Next Smaller Element
- Pattern 3: Previous Greater / Smaller Element
- Pattern 4: Histogram Expansion
- Pattern 5: Matrix → Histogram Trick
- Pattern 6: Monotonic Deque (Sliding Window)
- Pattern 7: Greedy Stack
- Pattern 8: Prefix Sum + Monotonic Deque
- Practice Roadmap
- Stack for State Management
- Stack Design
Parentheses Matching
When to use: matching brackets, nested structures
Use stack’s LIFO property to match opening and closing brackets in reverse order.
def is_valid_parentheses(s: str) -> bool:
st: list[str] = []
closing = {")": "(", "]": "[", "}": "{"}
for c in s:
if c in "({[":
st.append(c)
else:
if not st or st[-1] != closing[c]:
return False
st.pop()
return not st
| ID | Title | Link | Solution |
|---|---|---|---|
| 20 | Valid Parentheses | Link | Solution |
| 921 | Minimum Add to Make Valid Parentheses | Link | Solution |
| 1249 | Minimum Remove to Make Valid Parentheses | Link | Solution |
Expression Evaluation
When to use: calculate expression, operator precedence
Use stack to handle operator precedence and parentheses in mathematical expressions.
def calculate_basic(s: str) -> int:
stk: list[int] = []
result = num = 0
sign = 1
for c in s:
if c.isdigit():
num = num * 10 + int(c)
elif c in "+-":
result += sign * num
num = 0
sign = 1 if c == "+" else -1
elif c == "(":
stk.append(result)
stk.append(sign)
result = num = 0
sign = 1
elif c == ")":
result += sign * num
num = 0
result *= stk.pop()
result += stk.pop()
return result + sign * num
| ID | Title | Link | Solution |
|---|---|---|---|
| 150 | Evaluate Reverse Polish Notation | Link | Solution |
| 224 | Basic Calculator | Link | Solution |
| 227 | Basic Calculator II | Link | Solution |
| 772 | Basic Calculator III | Link | Solution |
Nested Structure Processing
Use stack to process nested structures like strings, expressions, or function calls.
def decode_string(s: str) -> str:
stack: list = []
cur = ""
k = 0
for c in s:
if c.isdigit():
k = k * 10 + int(c)
elif c == "[":
stack.append(cur)
stack.append(k)
cur, k = "", 0
elif c == "]":
repeat = stack.pop()
prev = stack.pop()
cur = prev + cur * repeat
else:
cur += c
return cur
| ID | Title | Link | Solution |
|---|---|---|---|
| 394 | Decode String | Link | Solution |
| 636 | Exclusive Time of Functions | Link | Solution |
| 71 | Simplify Path | Link | - |
Monotonic Stack & Deque Patterns
When to use: next greater/smaller element, histogram problems
Eight common patterns that cover nearly all monotonic stack / deque problems. Recognize the pattern by this clue: “find the next/previous smaller/greater element, or determine how far an element can extend.”
Pattern 1: Next Greater Element
Find the first element to the right that is strictly greater. Use a monotonic decreasing stack (top is smallest).
def next_greater_elements(nums: list[int]) -> list[int]:
n = len(nums)
result = [-1] * n
st: list[int] = []
for i in range(n):
while st and nums[st[-1]] < nums[i]:
result[st.pop()] = nums[i]
st.append(i)
return result
| ID | Title | Link | Solution |
|---|---|---|---|
| 496 | Next Greater Element I | Link | Solution |
| 739 | Daily Temperatures | Link | Solution |
| 503 | Next Greater Element II | Link | Solution |
| 901 | Online Stock Span | Link | - |
| 1944 | Visible People in Queue | Link | Solution |
Pattern 2: Next Smaller Element
Same idea but reversed comparison. Use a monotonic increasing stack (top is largest).
# Example: exclusive time of functions (simplified)
def exclusive_time(n: int, logs: list[str]) -> list[int]:
res = [0] * n
st: list[tuple[int, int]] = [] # (func_id, start_time)
for log in logs:
parts = log.split(":")
fid, typ, t = int(parts[0]), parts[1], int(parts[2])
if typ == "start":
st.append((fid, t))
else:
fid, start = st.pop()
duration = t - start + 1
res[fid] += duration
if st:
res[st[-1][0]] -= duration
return res
| ID | Title | Link | Solution |
|---|---|---|---|
| 1475 | Final Prices With Special Discount | Link | - |
| 84 | Largest Rectangle in Histogram | Link | Solution |
Pattern 3: Previous Greater / Smaller Element
Instead of looking right, find the left boundary. Scan left-to-right, the stack top is the previous greater/smaller.
Finding both previous smaller and next smaller defines the range where an element is the minimum – critical for range-based counting.
class MinStack:
def __init__(self) -> None:
self.stk: list[int] = []
self.min_stk: list[int] = []
def push(self, val: int) -> None:
self.stk.append(val)
self.min_stk.append(val if not self.min_stk else min(val, self.min_stk[-1]))
def pop(self) -> None:
self.stk.pop()
self.min_stk.pop()
def top(self) -> int:
return self.stk[-1]
def getMin(self) -> int:
return self.min_stk[-1]
| ID | Title | Link | Solution |
|---|---|---|---|
| 907 | Sum of Subarray Minimums | Link | - |
| 2104 | Sum of Subarray Ranges | Link | - |
Pattern 4: Histogram Expansion
Each bar expands left and right until hitting a shorter bar. Width = right_smaller - left_smaller - 1.
Combine next smaller (right boundary) and previous smaller (left boundary) to compute the maximum rectangle.
def largestRectangleArea(heights: list[int]) -> int:
n = len(heights)
ans = 0
st: list[int] = []
for i in range(n + 1):
h = 0 if i == n else heights[i]
while st and heights[st[-1]] > h:
height = heights[st.pop()]
width = i if not st else i - st[-1] - 1
ans = max(ans, height * width)
st.append(i)
return ans
| ID | Title | Link | Solution |
|---|---|---|---|
| 84 | Largest Rectangle in Histogram | Link | Solution |
| 42 | Trapping Rain Water | Link | Solution |
Pattern 5: Matrix → Histogram Trick
Convert each row of a binary matrix into a histogram of heights, then run the histogram algorithm on each row.
def maximalRectangle(matrix: list[list[str]]) -> int:
if not matrix:
return 0
m, n = len(matrix), len(matrix[0])
ans = 0
heights = [0] * n
for i in range(m):
for j in range(n):
heights[j] = heights[j] + 1 if matrix[i][j] == "1" else 0
ans = max(ans, largestRectangleArea(heights))
return ans
| ID | Title | Link | Solution |
|---|---|---|---|
| 85 | Maximal Rectangle | Link | - |
Pattern 6: Monotonic Deque (Sliding Window Max/Min)
When to use: sliding window maximum/minimum
Maintain a monotonic decreasing deque of indices for sliding window maximum. Remove smaller elements from back, remove out-of-window elements from front.
from collections import deque
def maxSlidingWindow(nums: list[int], k: int) -> list[int]:
dq: deque[int] = deque()
ans: list[int] = []
for i in range(len(nums)):
while dq and nums[dq[-1]] <= nums[i]:
dq.pop()
dq.append(i)
if dq[0] <= i - k:
dq.popleft()
if i >= k - 1:
ans.append(nums[dq[0]])
return ans
| ID | Title | Link | Solution |
|---|---|---|---|
| 239 | Sliding Window Maximum | Link | Solution |
Pattern 7: Greedy Stack (Remove Digits / Lexicographic Optimization)
When to use: remove digits, lexicographic optimization
Use the stack to maintain an optimal ordering. While the stack top is worse than the current element and we still have removals left, pop it.
def removeKdigits(num: str, k: int) -> str:
st: list[str] = []
for c in num:
while k > 0 and st and st[-1] > c:
st.pop()
k -= 1
st.append(c)
while k > 0:
st.pop()
k -= 1
# strip leading zeros
start = 0
while start < len(st) and st[start] == "0":
start += 1
ans = "".join(st[start:])
return "0" if not ans else ans
| ID | Title | Link | Solution |
|---|---|---|---|
| 402 | Remove K Digits | Link | - |
| 316 | Remove Duplicate Letters | Link | Solution |
Pattern 8: Prefix Sum + Monotonic Deque
Find the shortest subarray with sum at least k. Combine prefix sums with an increasing deque to efficiently find the closest valid left boundary.
from collections import deque
def shortestSubarray(nums: list[int], k: int) -> int:
n = len(nums)
ans = n + 1
pre = [0] * (n + 1)
for i in range(n):
pre[i + 1] = pre[i] + nums[i]
dq: deque[int] = deque()
for i in range(n + 1):
while dq and pre[i] - pre[dq[0]] >= k:
ans = min(ans, i - dq[0])
dq.popleft()
while dq and pre[dq[-1]] >= pre[i]:
dq.pop()
dq.append(i)
return ans if ans <= n else -1
| ID | Title | Link | Solution |
|---|---|---|---|
| 862 | Shortest Subarray with Sum at Least K | Link | Solution |
Practice Roadmap
Follow this progression from basics to advanced:
| Step | Focus | Problems |
|---|---|---|
| 1 | Basics | Next Greater Element I (496), Daily Temperatures (739) |
| 2 | Core Stack Mastery | Next Greater Element II (503), Online Stock Span (901) |
| 3 | Histogram | Largest Rectangle in Histogram (84), Maximal Rectangle (85) |
| 4 | Advanced Range Counting | Sum of Subarray Minimums (907), Sum of Subarray Ranges (2104) |
| 5 | Deque + Advanced | Sliding Window Maximum (239), Shortest Subarray with Sum at Least K (862) |
Stack for State Management
Use stack to save and restore state when processing nested or hierarchical structures.
# Example: Tracking function call stack
def processLogs(logs: list[str]) -> None:
st: list[tuple[int, int]] = [] # {function_id, start_time}
result = [0] * n
for log in logs:
# Parse log entry
if isStart:
st.append((id, time))
else:
funcId, startTime = st.pop()
duration = time - startTime + 1
result[funcId] += duration
# Subtract from parent if exists
if st:
result[st[-1][0]] -= duration
| ID | Title | Link | Solution |
|---|---|---|---|
| 636 | Exclusive Time of Functions | Link | Solution |
| 394 | Decode String | Link | Solution |
Stack Design (Min/Max Stack)
Maintaining extra information (like minimums or frequencies) alongside the primary stack data.
class MinStack:
def __init__(self):
self.stk: list[int] = []
self.minStk: list[int] = []
def push(self, val: int) -> None:
self.stk.append(val)
if not self.minStk:
self.minStk.append(val)
else:
self.minStk.append(min(self.minStk[-1], val))
def pop(self) -> None:
self.stk.pop()
self.minStk.pop()
def top(self) -> int:
return self.stk[-1]
def getMin(self) -> int:
return self.minStk[-1]
| ID | Title | Link | Solution |
|---|---|---|---|
| 155 | Min Stack | Link | Solution |
| 716 | Max Stack | Link | - |
Key Patterns
- LIFO Property: Stack naturally handles reverse-order matching (parentheses, brackets)
- State Preservation: Save state before entering nested structures, restore after exiting
- Operator Precedence: Use stack to defer low-precedence operations
- Monotonic Order: Maintain sorted order to efficiently find extrema
- Index Tracking: Store indices instead of values when you need position information
When to Use Stack
- ✅ Matching problems (parentheses, brackets, tags)
- ✅ Expression evaluation with precedence
- ✅ Nested structure processing
- ✅ Finding next/previous greater/smaller elements
- ✅ Reversing order or processing in reverse
- ✅ Undo/redo functionality
- ✅ Function call tracking
Common Mistakes
- Forgetting to check empty stack before
st.top()orst.pop() - Wrong stack order when pushing/popping multiple values
- Not resetting state after processing elements
- Index vs value confusion in monotonic stack problems
Quick Reference
| Pattern | Signal Phrases | Key Idea |
|---|---|---|
| Parentheses | “valid brackets”, “minimum remove” | Match open/close pairs |
| Expression | “calculate”, “evaluate” | Operator precedence via stack |
| Next Greater | “next greater”, “next warmer day” | Monotonic decreasing stack |
| Next Smaller | “next smaller”, “stock span” | Monotonic increasing stack |
| Histogram | “largest rectangle”, “maximal rectangle” | Expand left/right using stack |
| Sliding Window Max | “maximum in window” | Monotonic deque |
More templates
- Beginner’s Guide: LeetCode Beginner’s Guide
- Data structures (monotonic stack/queue): Data Structures & Core Algorithms
- Graph, Search: Graph, Search
- Master index: Categories & Templates