Understanding how a Stack works and how to build one using a Vector, Array, or Linked List gives you the foundation. However, in competitive programming and technical interviews, the real challenge is recognizing when a problem is secretly a Stack problem—and knowing how to use techniques like Monotonic Stacks to turn a slow brute-force loop into a fast solution.
In this guide, we will solve 5 famous Stack problems carefully selected so that each problem teaches you a completely different Stack pattern:
- Valid Parentheses — Nested Pair Matching
- Min Stack — Auxiliary State Tracking in
- Next Greater Element — The Monotonic Decreasing Stack Pattern
- Daily Temperatures — Storing Array Indices in a Monotonic Stack
- Largest Rectangle in Histogram — Two-Sided Boundary Expansion with a Monotonic Increasing Stack
Part of the Stack Series: This article is Step 5 of our Stack in C++: Complete Beginner-to-Advanced Guide.
Problem 1: Valid Parentheses
- Platform & Link: LeetCode #20 — Valid Parentheses
- Core Technique: Nested Bracket Matching using LIFO
1. Problem Statement Summary
Given a string s containing only the bracket characters '(', ')', '{', '}', '[', and ']', determine whether the input string is valid.
A string is valid if:
- Every opening bracket is closed by the same type of closing bracket.
- Opening brackets are closed in the correct nested order.
- Every closing bracket has a corresponding opening bracket before it.
2. Example Input and Output
- Example 1:
s = "{[()]}"Output:Valid(true) - Example 2:
s = "([)]"Output:Invalid(false—[was opened inside(, so]must close before)) - Example 3:
s = "((("Output:Invalid(false— unclosed opening brackets remain)
3. Why Is a Stack Useful Here?
When you read a nested expression from left to right, the last bracket you open must be the first bracket you close:
String: { [ ( ) ] }
| | | ^ ^ ^
| | +---+ | | <-- Innermost '(' opened LAST, closed FIRST
| +-----------+ | <-- Middle '[' closed SECOND
+-------------------+ <-- Outermost '{' opened FIRST, closed LAST
“Last Opened, First Closed” is literally Last In, First Out (LIFO)!
4. Step-by-Step Approach
- Create an empty stack of characters:
stack<char> st;. - Loop through every character
chin the strings:- If
chis an opening bracket ('(','{', or'['), push it onto the stack. - If
chis a closing bracket (')','}', or']'):- First check if the stack is empty. If it is empty, there is no matching opening bracket—return
falseimmediately. - Otherwise, inspect
st.top(). Ifst.top()is the matching opening bracket forch, callst.pop(). If it does not match, returnfalse.
- First check if the stack is empty. If it is empty, there is no matching opening bracket—return
- If
- After the loop finishes, check
st.empty(). If the stack is completely empty, every opening bracket was matched and closed (true). If any brackets remain in the stack, returnfalse.
5. Complete C++ Solution
#include <iostream>
#include <stack>
#include <string>
using namespace std;
bool isValidParentheses(string s) {
stack<char> st;
for (int i = 0; i < s.length(); i++) {
char ch = s[i];
// 1. If it is an opening bracket, push onto stack
if (ch == '(' || ch == '{' || ch == '[') {
st.push(ch);
}
// 2. Otherwise it is a closing bracket
else {
// If stack is empty, there is no opening bracket to match with
if (st.empty()) {
return false;
}
char topBracket = st.top();
// Check if the closing bracket matches the most recent opening bracket
if ((ch == ')' && topBracket == '(') ||
(ch == '}' && topBracket == '{') ||
(ch == ']' && topBracket == '[')) {
st.pop();
} else {
return false; // Mismatched bracket types
}
}
}
// Valid only if no unclosed opening brackets remain
if (st.empty()) {
return true;
} else {
return false;
}
}
int main() {
string test1 = "{[()]}";
string test2 = "([)]";
if (isValidParentheses(test1)) {
cout << test1 << " is Valid" << endl;
} else {
cout << test1 << " is Invalid" << endl;
}
if (isValidParentheses(test2)) {
cout << test2 << " is Valid" << endl;
} else {
cout << test2 << " is Invalid" << endl;
}
return 0;
}
6. Output
{[()]} is Valid
([)] is Invalid
7. Detailed Code Explanation
stack<char> st;: Holds all opening brackets that are still waiting to be closed.if (st.empty()) return false;: Handles inputs like")()"where a closing bracket appears when nothing is open.char topBracket = st.top();: Reads the most recently opened bracket. If it matches the current closing bracketch,st.pop()removes that opening bracket because its pair is now complete.if (st.empty()) return true;: Ensures inputs like"((("returnfalsebecause unclosed brackets are still sitting insidest.
8. Dry Run (s = "{[()]}")
| Step | Character ch | Action Taken | Stack State (Bottom Top) |
|---|---|---|---|
| 1 | '{' | Opening bracket push('{') | ['{'] |
| 2 | '[' | Opening bracket push('[') | ['{', '['] |
| 3 | '(' | Opening bracket push('(') | ['{', '[', '('] |
| 4 | ')' | Matches top() '(' pop() | ['{', '['] |
| 5 | ']' | Matches top() '[' pop() | ['{'] |
| 6 | '}' | Matches top() '{' pop() | [ ] (Empty Returns true) |
9. Complexity, Edge Cases, and Takeaway
- Time Complexity: , where is the length of the string, because we visit each character once and perform stack operations.
- Space Complexity: in the worst case (e.g.,
"((((((("), where all characters are pushed onto the stack. - Common Mistakes & Edge Cases:
- Forgetting to check
if (st.empty())before callingst.top()when a closing bracket arrives (causes a runtime crash on"]"). - Returning
trueat the end of the loop without checkingst.empty()(fails on"(((").
- Forgetting to check
- What You Learn: Whenever a problem involves nested structures (parentheses, HTML/XML tags, nested folders) where the most recently opened item must be closed first, use a Stack.
Problem 2: Min Stack
- Platform & Link: LeetCode #155 — Min Stack
- Core Technique: Maintaining Auxiliary State in Time
1. Problem Statement Summary
Design a stack class MinStack that supports all standard stack operations (push, pop, top) plus a getMin() function that retrieves the minimum element currently in the stack—and every single operation must run in constant time.
2. Example Input and Output
push(-2),push(0),push(-3)getMin()returns-3pop()(removes-3)top()returns0getMin()returns-2
3. Brute-Force vs. Why a Second Stack Is Useful
- Brute-Force Idea: Keep a single variable
int minVal. That works when pushing, but what happens when the minimum element is popped? Finding the new minimum across the remaining elements would require scanning the entire stack in time, violating the rule! - Stack Insight: The minimum of a stack only changes when elements are pushed or popped at the top. If we maintain a second stack (
minStack) whose top always stores the minimum value of the stack up to that depth, we can restore the previous minimum in time whenever we pop!
Operations: push(5), push(2), push(4), push(1)
mainStack (Actual Values): minStack (Minimum So Far at Each Level):
+---+ +---+
| 1 | <-- TOP | 1 | <-- Current Min = min(1, 2) = 1
+---+ +---+
| 4 | | 2 | <-- Min when 4 was pushed = min(4, 2) = 2
+---+ +---+
| 2 | | 2 | <-- Min when 2 was pushed = min(2, 5) = 2
+---+ +---+
| 5 | | 5 | <-- Min when 5 was pushed = 5
+---+ +---+
If we pop() the top from both stacks, 1 is removed and minStack.top() immediately reveals that the minimum of the remaining elements is 2!
4. Step-by-Step Approach
- Maintain two stacks inside the
MinStackclass:stack<int> mainStack;andstack<int> minStack;. push(int val):- Push
valontomainStack. - If
minStackis empty orval < minStack.top(), pushvalontominStack. Otherwise, pushminStack.top()again ontominStackso both stacks always have the exact same height.
- Push
pop(): Pop the top element from bothmainStackandminStack.top(): ReturnmainStack.top().getMin(): ReturnminStack.top().
5. Complete C++ Solution
#include <iostream>
#include <stack>
using namespace std;
class MinStack {
private:
stack<int> mainStack;
stack<int> minStack;
public:
// Push value onto the stack and update the minimum history
void push(int val) {
mainStack.push(val);
if (minStack.empty()) {
minStack.push(val);
} else {
int currentMin = minStack.top();
if (val < currentMin) {
minStack.push(val);
} else {
minStack.push(currentMin);
}
}
}
// Remove the top element from both stacks
void pop() {
if (mainStack.empty()) {
cout << "Stack is empty!" << endl;
return;
}
mainStack.pop();
minStack.pop();
}
// Get the top element
int top() {
return mainStack.top();
}
// Retrieve the minimum element in the stack in O(1) time
int getMin() {
return minStack.top();
}
};
int main() {
MinStack ms;
ms.push(-2);
ms.push(0);
ms.push(-3);
cout << "Current Minimum: " << ms.getMin() << endl; // -3
ms.pop(); // Removes -3
cout << "Top Element: " << ms.top() << endl; // 0
cout << "New Minimum: " << ms.getMin() << endl; // -2
return 0;
}
6. Output
Current Minimum: -3
Top Element: 0
New Minimum: -2
7. Dry Run
| Step | Operation | mainStack (Bottom Top) | minStack (Bottom Top) | Output |
|---|---|---|---|---|
| 1 | push(-2) | [-2] | [-2] | — |
| 2 | push(0) | [-2, 0] | [-2, -2] (since 0 >= -2) | — |
| 3 | push(-3) | [-2, 0, -3] | [-2, -2, -3] (since -3 < -2) | — |
| 4 | getMin() | [-2, 0, -3] | [-2, -2, -3] | -3 |
| 5 | pop() | [-2, 0] | [-2, -2] | — |
| 6 | getMin() | [-2, 0] | [-2, -2] | -2 |
8. Complexity, Edge Cases, and Takeaway
- Time Complexity: for
push(),pop(),top(), andgetMin(). - Space Complexity: to store the elements in
mainStackandminStack. - Common Mistakes & Edge Cases: If you optimize
minStackto only push when a new minimum appears, you must useval <= minStack.top()(including=) so duplicate minimums (like pushing-2twice and popping once) don’t accidentally erase the minimum! Pushing a minimum at every level as shown above avoids that bug completely. - What You Learn: You can augment a Stack to answer prefix/aggregate queries (
getMin(),getMax()) in time by storing the running answer alongside each stack level.
Problem 3: Next Greater Element
- Platform & Links:
- LeetCode #496 — Next Greater Element I
- Related Classic Version: CSES Problem Set — Nearest Smaller Values
- Core Technique: The Monotonic Stack Pattern
1. Problem Statement Summary
Given an array arr of integers, for every element arr[i], find the Next Greater Element (NGE)—the first element to its right that is strictly greater than arr[i]. If no larger element exists to its right, output -1 for that position.
2. Example Input and Output
- Input:
arr = [4, 5, 2, 25] - Output:
[5, 25, 25, -1]- Next greater to the right of
4is5. - Next greater to the right of
5is25. - Next greater to the right of
2is25. 25has no greater element to its right-1.
- Next greater to the right of
3. Brute-Force Approach vs. Why a Stack Is Useful
- Brute-Force (): Use two nested loops. For each index
i, scanjfromi + 1ton - 1until you findarr[j] > arr[i]. On an array of size , operations, which fails with Time Limit Exceeded (TLE). - Why a Monotonic Stack Works in :
Suppose we scan the array from right to left and keep a stack of “candidate greater elements” we have seen so far.
When we are at
arr[i], any element sitting on top of the stack that is less than or equal toarr[i]is useless! Why? Becausearr[i]is both larger than those stack elements and closer to any future elements on the left. So we can permanentlypop()all elements !
Scanning Right-to-Left on [4, 5, 2, 25]:
When at 5 (looking right):
Stack before: Top -> [2, 25]
1. Is top (2) <= 5? YES! Pop 2 (2 can never be the Next Greater for 5 or anything left of 5).
2. Is top (25) <= 5? NO! Stop popping. Top (25) is the Next Greater Element for 5!
3. Push 5 onto stack: Top -> [5, 25]
Because elements in the stack stay strictly sorted from top to bottom, this is called a Monotonic Stack.
4. Step-by-Step Approach
- Create a result vector
nge(n, -1)and an emptystack<int> st. - Loop
ibackwards fromn - 1down to0:- Step A: While
!st.empty()andst.top() <= arr[i], callst.pop()to discard smaller or equal elements. - Step B: If
!st.empty(), thenst.top()is the first element to the right that is strictly greater thanarr[i], so setnge[i] = st.top(). (If the stack is empty,nge[i]stays-1.) - Step C: Push
arr[i]ontostso it can serve as a candidate for elements further to the left.
- Step A: While
5. Complete C++ Solution
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
vector<int> findNextGreaterElements(vector<int>& arr) {
int n = arr.size();
vector<int> nge(n, -1);
stack<int> st;
// Traverse from right to left
for (int i = n - 1; i >= 0; i--) {
// 1. Pop elements that are smaller than or equal to arr[i]
while (!st.empty() && st.top() <= arr[i]) {
st.pop();
}
// 2. If stack is not empty, the top element is the Next Greater Element
if (!st.empty()) {
nge[i] = st.top();
}
// 3. Push current element onto stack as a candidate for left elements
st.push(arr[i]);
}
return nge;
}
int main() {
vector<int> arr = {4, 5, 2, 25};
vector<int> result = findNextGreaterElements(arr);
cout << "Element -> Next Greater Element" << endl;
for (int i = 0; i < arr.size(); i++) {
cout << arr[i] << " -> " << result[i] << endl;
}
return 0;
}
6. Output
Element -> Next Greater Element
4 -> 5
5 -> 25
2 -> 25
25 -> -1
7. Dry Run (arr = [4, 5, 2, 25], Right to Left)
Index i | arr[i] | Elements Popped (<= arr[i]) | Stack Top After Pops (nge[i]) | Push arr[i] Stack (Bottom Top) |
|---|---|---|---|---|
3 | 25 | None | Empty (-1) | [25] |
2 | 2 | None (25 > 2) | 25 | [25, 2] |
1 | 5 | Pops 2 (2 <= 5) | 25 | [25, 5] |
0 | 4 | None (5 > 4) | 5 | [25, 5, 4] |
8. Complexity, Edge Cases, and Takeaway
- Time Complexity: . Even though there is a
whileloop inside aforloop, every element is pushed onto the stack once and popped at most once across the entire algorithm ( total operations). - Space Complexity: for the stack and output vector.
- Common Mistakes & Edge Cases: Using
<instead of<=insidewhile (!st.empty() && st.top() <= arr[i])when duplicate numbers exist (e.g.,[4, 4, 5]—the next strictly greater element for the first4is5, not4). - What You Learn: The Monotonic Stack pattern finds the nearest greater or smaller neighbor to the left or right of every element in an array in linear time.
Problem 4: Daily Temperatures
- Platform & Link: LeetCode #739 — Daily Temperatures
- Core Technique: Monotonic Stack Storing Array Indices Instead of Values
1. Problem Statement Summary
Given an array of integers temperatures representing the daily temperature on each day, return an array answer such that answer[i] is the number of days you have to wait after day i to get a warmer temperature. If there is no future day with a warmer temperature, answer[i] should be 0.
2. Example Input and Output
- Input:
temperatures = [73, 74, 75, 71, 69, 72, 76, 73] - Output:
[1, 1, 4, 2, 1, 1, 0, 0]- From day 0 (
73), day 1 (74) is warmer1 - 0 = 1day wait. - From day 2 (
75), day 6 (76) is the first warmer day6 - 2 = 4days wait.
- From day 0 (
3. Why Storing Indices in a Stack Is Useful
Notice that this problem is just like Next Greater Element, with one twist: instead of asking what the next greater temperature is, it asks how many indices away that warmer day is (nextWarmerIndex - currentIndex)!
If we push array indices (0, 1, 2, ...) onto our stack instead of raw temperatures:
- We can still look up the temperature of the top index using
temperatures[st.top()]. - We can also compute the exact distance between days by subtracting indices:
i - st.top()!
Day Index: 0 1 2 3 4 5 6 7
Temperature: [73, 74, 75, 71, 69, 72, 76, 73]
| ^
+--- 6 - 2 = 4 days +
4. Step-by-Step Approach (Left-to-Right Monotonic Stack)
- Create
vector<int> waitDays(n, 0)andstack<int> st(which stores day indices waiting for a warmer day). - Loop
ifrom0ton - 1:- While the stack is not empty and today’s temperature
temperatures[i]is strictly warmer than the temperature on dayst.top()(temperatures[i] > temperatures[st.top()]):- Let
prevDay = st.top();andst.pop();. - Today (
i) is the first warmer day forprevDay! SetwaitDays[prevDay] = i - prevDay;.
- Let
- Push today’s index
iontost.
- While the stack is not empty and today’s temperature
- Any index still left in
stat the end never saw a warmer day, and itswaitDaysentry is already0.
5. Complete C++ Solution
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size();
vector<int> waitDays(n, 0);
stack<int> st; // Stores indices of days waiting for a warmer temperature
for (int i = 0; i < n; i++) {
// Check if today's temperature resolves any cooler days waiting on the stack
while (!st.empty() && temperatures[i] > temperatures[st.top()]) {
int prevDayIndex = st.top();
st.pop();
waitDays[prevDayIndex] = i - prevDayIndex;
}
// Push today's index onto the stack
st.push(i);
}
return waitDays;
}
int main() {
vector<int> temperatures = {73, 74, 75, 71, 69, 72, 76, 73};
vector<int> result = dailyTemperatures(temperatures);
cout << "Wait days for each day: ";
for (int i = 0; i < result.size(); i++) {
cout << result[i] << " ";
}
cout << endl;
return 0;
}
6. Output
Wait days for each day: 1 1 4 2 1 1 0 0
7. Dry Run (temperatures = [73, 74, 75, 71, 69, 72, 76, 73])
Day i | temp[i] | Days Resolved & Popped (temp[i] > temp[top]) | Stack of Indices (temp) After Push |
|---|---|---|---|
0 | 73 | None | [0 (73)] |
1 | 74 | Pops 0: waitDays[0] = 1 - 0 = 1 | [1 (74)] |
2 | 75 | Pops 1: waitDays[1] = 2 - 1 = 1 | [2 (75)] |
3 | 71 | None (71 <= 75) | [2 (75), 3 (71)] |
4 | 69 | None (69 <= 71) | [2 (75), 3 (71), 4 (69)] |
5 | 72 | Pops 4 (5-4=1), Pops 3 (5-3=2) | [2 (75), 5 (72)] |
6 | 76 | Pops 5 (6-5=1), Pops 2 (6-2=4) | [6 (76)] |
7 | 73 | None (73 <= 76) | [6 (76), 7 (73)] |
8. Complexity, Edge Cases, and Takeaway
- Time Complexity: because each index
0 .. n-1is pushed once and popped at most once. - Space Complexity: for the stack of indices.
- Common Mistakes & Edge Cases: Pushing
temperatures[i]instead of the indexionto the stack—if you only store the temperature value, you lose the position needed to computei - prevDayIndex. - What You Learn: Almost always store indices in a Monotonic Stack rather than raw values. If you have the index
idx, you automatically have both the position (idx) and the value (arr[idx])!
Problem 5: Largest Rectangle in Histogram
- Platform & Link: LeetCode #84 — Largest Rectangle in Histogram
- Core Technique: Monotonic Increasing Stack for Left & Right Span Boundaries
1. Problem Statement Summary
Given an array of integers heights representing the heights of bars in a histogram where the width of each bar is 1, find the area of the largest rectangle that can be formed completely inside the histogram.
2. Example Input and Output
- Input:
heights = [2, 1, 5, 6, 2, 3] - Output:
10
6 | +---+
5 | +---| |
4 | | | |
3 | |///|///| +---+
2 | +---+ |///|///|---| |
1 | | |---|/|///| | |
+---+---+---+---+---+---+
Bars: 2 1 5 6 2 3
Indices: 0 1 2 3 4 5
Largest rectangle uses bars at index 2 (height 5) and index 3 (height 6):
Height = 5, Width = 2 (indices 2 and 3) -> Area = 5 * 2 = 10
3. Brute-Force vs. Why a Stack Is Useful
- Key Observation: Every largest rectangle has a height equal to at least one bar
heights[h]that acts as the shortest (limiting) bar in that rectangle. - Brute-Force (): For every bar
i, expand a pointerleftto the left andrightto the right as long as neighbouring bars haveheight >= heights[i]. Then computearea = heights[i] * (right - left - 1). - Optimized Stack Insight ():
How far can bar
hexpand? It can expand left until it hits the first bar strictly shorter thanhon the left, and it can expand right until it hits the first bar strictly shorter thanhon the right! If we maintain a Monotonic Increasing Stack of indices as we scan left to right:- Whenever we encounter a new bar
ithat is shorter than the bar atst.top(), bariblocksst.top()from expanding any further to the right! - So
iis the Right Boundary forst.top(). - And what is the Left Boundary for
st.top()? It is the index sitting directly belowst.top()in our monotonic increasing stack!
- Whenever we encounter a new bar
When bar[i] < bar[st.top()]:
Popped bar index = h
Right boundary (first shorter bar on right) = i
Left boundary (first shorter bar on left) = new st.top() (or -1 if stack is empty)
Width of rectangle with height heights[h] = i - leftBoundary - 1
4. Step-by-Step Approach
- Maintain an empty
stack<int> stof bar indices andint maxArea = 0. - Loop
ifrom0ton(wherei == nacts as a virtual bar of height0at the end to flush all remaining bars out of the stack):- Set
currentHeight = 0ifi == n, otherwiseheights[i]. - While
!st.empty()andcurrentHeight < heights[st.top()]:- Pop the top index:
int hIndex = st.top(); st.pop(); int rectHeight = heights[hIndex];- Determine the width:
- If
st.empty()is true, no bar to the left was shorter, so the rectangle spans all the way from0toi - 1(width = i). - Otherwise, the rectangle spans between
st.top()andi(width = i - st.top() - 1).
- If
- Update
maxAreaifrectHeight * width > maxArea.
- Pop the top index:
- Push
iontost.
- Set
5. Complete C++ Solution
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
int largestRectangleArea(vector<int>& heights) {
int n = heights.size();
stack<int> st; // Monotonic increasing stack of bar indices
int maxArea = 0;
// Loop from 0 to n (i == n represents a virtual height-0 bar at the end)
for (int i = 0; i <= n; i++) {
int currentHeight = 0;
if (i < n) {
currentHeight = heights[i];
}
// When current bar is shorter than stack top, compute area for stack top
while (!st.empty() && currentHeight < heights[st.top()]) {
int topIndex = st.top();
st.pop();
int rectHeight = heights[topIndex];
int rectWidth = 0;
if (st.empty()) {
rectWidth = i; // Extends all the way to index 0
} else {
rectWidth = i - st.top() - 1; // Between st.top() and i
}
int area = rectHeight * rectWidth;
if (area > maxArea) {
maxArea = area;
}
}
st.push(i);
}
return maxArea;
}
int main() {
vector<int> heights = {2, 1, 5, 6, 2, 3};
int result = largestRectangleArea(heights);
cout << "Largest Rectangle Area: " << result << endl;
return 0;
}
6. Output
Largest Rectangle Area: 10
7. Dry Run (heights = [2, 1, 5, 6, 2, 3])
Index i | currentHeight | Popped topIndex (rectHeight) | New st.top() (Left Bound) | rectWidth | area | maxArea | Stack After Push |
|---|---|---|---|---|---|---|---|
0 | 2 | None | — | — | — | 0 | [0] |
1 | 1 | Pops 0 (h=2) | Empty | 1 | 2 * 1 = 2 | 2 | [1] |
2 | 5 | None (5 >= 1) | — | — | — | 2 | [1, 2] |
3 | 6 | None (6 >= 5) | — | — | — | 2 | [1, 2, 3] |
4 | 2 | 1) Pops 3 (h=6)2) Pops 2 (h=5) | 21 | 4 - 2 - 1 = 14 - 1 - 1 = 2 | 6 * 1 = 65 * 2 = 10 | 610 | [1, 4] |
5 | 3 | None (3 >= 2) | — | — | — | 10 | [1, 4, 5] |
6 (end) | 0 | 1) Pops 5 (h=3)2) Pops 4 (h=2)3) Pops 1 (h=1) | 41Empty | 6 - 4 - 1 = 16 - 1 - 1 = 46 | 3 * 1 = 32 * 4 = 81 * 6 = 6 | 10 | [6] |
8. Complexity, Edge Cases, and Takeaway
- Time Complexity: . Every bar index from
0tonis pushed once and popped at most once. - Space Complexity: for the stack.
- Common Mistakes & Edge Cases:
- Forgetting to process the bars remaining in the stack after the last index
n - 1(thei == nvirtual0-height bar trick handles this cleanly in a single loop). - Using
i - topIndexas the width instead ofi - st.top() - 1(because a popped bar can also extend leftward across taller bars that were already popped before it!).
- Forgetting to process the bars remaining in the stack after the last index
- What You Learn: A Monotonic Increasing Stack gives you both the nearest smaller element on the left (
st.top()after popping) and the nearest smaller element on the right (i) for every element in a single pass!
Summary Table of the 5 Stack Patterns
| Problem | Stack Pattern Learned | Time | Space |
|---|---|---|---|
| 1. Valid Parentheses | LIFO Nested Pair Matching | ||
| 2. Min Stack | Auxiliary Stack for Prefix Minimums | ||
| 3. Next Greater Element | Monotonic Decreasing Stack (Values) | ||
| 4. Daily Temperatures | Monotonic Decreasing Stack (Indices & Distances) | ||
| 5. Largest Rectangle in Histogram | Monotonic Increasing Stack (Left & Right Span Boundaries) |
Frequently Asked Questions (FAQ)
How do I know whether to use a Monotonic Increasing or Decreasing Stack?
- To find the Next Greater element, keep a Monotonic Decreasing stack (from bottom to top), popping whenever a larger element arrives.
- To find the Next Smaller element (like in Largest Rectangle in Histogram), keep a Monotonic Increasing stack, popping whenever a smaller element arrives.
Why is a Monotonic Stack even though it has a while loop inside a for loop?
Because an element is never pushed back after it is popped. Across all iterations of the for loop, st.push() runs times and st.pop() can run at most times in total. .
Review the Complete Stack Curriculum
Congratulations on completing the entire Stack in C++ series! You can revisit any article in the learning path below:
- Topic Hub: Stack in C++: Complete Beginner-to-Advanced Guide
- Step 1: Understanding Stack in C++: Complete Beginner’s Guide
- Step 2: Stack Using Vector in C++: Step-by-Step Implementation
- Step 3: Stack Using Array in C++: Fixed-Size Implementation & Dry Run
- Step 4: Stack Using Linked List in C++: Dynamic Node Implementation
- Related Data Structure: The Queue Data Structure in C
- Explore All Algorithms: Data Structures & Algorithmic Engineering Hub