Latest commit

History

History
440 lines (357 loc) · 11.6 KB

File metadata and controls

440 lines (357 loc) · 11.6 KB

Stack Pattern

Overview

The stack pattern is one of the most fundamental and widely used patterns in computer science. It follows the Last In, First Out (LIFO) principle, making it perfect for problems involving nested structures, expression evaluation, and backtracking.

When to Use Stack Pattern

1. Expression Evaluation

  • Reverse Polish Notation (RPN) evaluation
  • Infix to Postfix conversion
  • Parentheses matching and validation
  • Calculator implementations

2. Nested Structure Processing

  • Parentheses matching (valid parentheses, nested brackets)
  • HTML/XML tag matching
  • Function call tracking
  • Nested object processing

3. Backtracking and Undo Operations

  • Browser history (back button functionality)
  • Text editor undo/redo
  • Game state management
  • Recursive algorithm simulation
  • Generate Parentheses (iterative approach)

4. Monotonic Stack Problems

  • Next greater element problems
  • Largest rectangle in histogram
  • Stock span problems
  • Temperature problems

📊 Visual Guide

Monotonic Stack (Next Greater Element)

Maintains a decreasing sequence to find the next larger element.

graph TD
A[Start: Iterate nums] --> B{Stack Empty?}
B -- Yes --> C[Push Current Index]
B -- No --> D{nums[Top] < Current?}
D -- No (Keep Order) --> C
D -- Yes (Found Next Greater) --> E[Pop Top]
E --> F[Record Answer for Top]
F --> B
Loading

⚖️ Stack Variations Comparison

Stack TypeInvariant PropertyPrimary Use CaseExample
Standard LIFONoneNested structures, Undo/RedoValid Parentheses
Monotonic IncreasingElements increase bottom-to-topNext Smaller ElementLargest Rectangle Area
Monotonic DecreasingElements decrease bottom-to-topNext Greater ElementDaily Temperatures
Min/Max StackAuxiliary stack tracks min/maxO(1) Min/Max RetrievalMin Stack

Core Stack Operations

#include<stack>
#include<iostream>usingnamespacestd;voiddemonstrateStackOperations() {
stack<int> st;
// Push elements
st.push(1);
st.push(2);
st.push(3);
// Check if empty
cout << "Is empty: " << st.empty() << endl; // 0 (false)// Get size
cout << "Size: " << st.size() << endl; // 3// Access top element
cout << "Top element: " << st.top() << endl; // 3// Pop element
st.pop();
cout << "After pop, top: " << st.top() << endl; // 2// Pop all elementswhile (!st.empty()) {
cout << "Popping: " << st.top() << endl;
st.pop();
}
}

Pattern 1: Expression Evaluation

Reverse Polish Notation (RPN)

classSolution {
public:intevalRPN(vector<string>& tokens) {
stack<int> st;
for (const string& token : tokens) {
if (token == "+" || token == "-" || token == "*" || token == "/") {
int b = st.top(); st.pop();
int a = st.top(); st.pop();
if (token == "+") st.push(a + b);
elseif (token == "-") st.push(a - b);
elseif (token == "*") st.push(a * b);
elseif (token == "/") st.push(a / b);
} else {
st.push(stoi(token));
}
}
return st.top();
}
};

Key Insights for RPN

  • Operand order matters: Second popped operand is the right operand
  • Stack naturally handles the LIFO nature of operations
  • Division truncates toward zero as required

Pattern 2: Parentheses Matching

Valid Parentheses

classSolution {
public:boolisValid(string s) {
stack<char> st;
for (char c : s) {
if (c == '(' || c == '[' || c == '{') {
st.push(c);
} else {
if (st.empty()) returnfalse;
char top = st.top();
st.pop();
if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
returnfalse;
}
}
}
return st.empty();
}
};

Key Insights for Parentheses

  • Opening brackets go on stack
  • Closing brackets must match the most recent opening bracket
  • Stack empty check is crucial for valid sequences

Pattern 3: Monotonic Stack

Next Greater Element

classSolution {
public:
vector<int> nextGreaterElement(vector<int>& nums) {
int n = nums.size();
vector<int> result(n, -1);
stack<int> st; // Store indicesfor (int i = 0; i < n; i++) {
while (!st.empty() && nums[st.top()] < nums[i]) {
result[st.top()] = nums[i];
st.pop();
}
st.push(i);
}
return result;
}
};

Key Insights for Monotonic Stack

  • Maintain decreasing order in stack
  • Process elements that are smaller than current
  • Store indices for result mapping

Pattern 4: Nested Structure Processing

Simplify Path

classSolution {
public:
string simplifyPath(string path) {
stack<string> st;
stringstream ss(path);
string token;
while (getline(ss, token, '/')) {
if (token == "" || token == ".") continue;
if (token == "..") {
if (!st.empty()) st.pop();
} else {
st.push(token);
}
}
if (st.empty()) return"/";
string result = "";
while (!st.empty()) {
result = "/" + st.top() + result;
st.pop();
}
return result;
}
};

Common Stack Patterns

1. Two-Stack Approach

// Min Stack implementationclassMinStack {
private:
stack<int> data;
stack<int> minStack;
public:voidpush(int val) {
data.push(val);
if (minStack.empty() || val <= minStack.top()) {
minStack.push(val);
}
}
voidpop() {
if (data.top() == minStack.top()) {
minStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMin() { return minStack.top(); }
};

2. Stack with Additional Data

// Stack with getMax() in O(1)classMaxStack {
private:
stack<int> data;
stack<int> maxStack;
public:voidpush(int val) {
data.push(val);
if (maxStack.empty() || val >= maxStack.top()) {
maxStack.push(val);
}
}
voidpop() {
if (data.top() == maxStack.top()) {
maxStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMax() { return maxStack.top(); }
};

Advanced Stack Applications

1. Largest Rectangle in Histogram

classSolution {
public:intlargestRectangleArea(vector<int>& heights) {
stack<int> st;
int maxArea = 0;
int n = heights.size();
for (int i = 0; i <= n; i++) {
int h = (i == n) ? 0 : heights[i];
while (!st.empty() && heights[st.top()] > h) {
int height = heights[st.top()];
st.pop();
int width = st.empty() ? i : i - st.top() - 1;
maxArea = max(maxArea, height * width);
}
st.push(i);
}
return maxArea;
}
};

2. Generate Parentheses (Iterative Approach)

classSolution {
public:
vector<string> generateParenthesis(int n) {
vector<string> results;
stack<tuple<string, int, int>> st;
// Push initial state: (current_string, open_count, close_count)
st.push(make_tuple("", 0, 0));
while (!st.empty()) {
auto [current, open, close] = st.top();
st.pop();
// Base case: we've generated a complete valid combinationif (open == close && close == n) {
results.push_back(current);
continue;
}
// Add opening parenthesis if we haven't used all nif (open < n) {
st.push(make_tuple(current + "(", open + 1, close));
}
// Add closing parenthesis if we have more '(' than ')'if (close < open) {
st.push(make_tuple(current + ")", open, close + 1));
}
}
return results;
}
};

3. Trapping Rain Water

classSolution {
public:inttrap(vector<int>& height) {
stack<int> st;
int water = 0;
for (int i = 0; i < height.size(); i++) {
while (!st.empty() && height[st.top()] < height[i]) {
int top = st.top();
st.pop();
if (st.empty()) break;
int distance = i - st.top() - 1;
int boundedHeight = min(height[i], height[st.top()]) - height[top];
water += distance * boundedHeight;
}
st.push(i);
}
return water;
}
};

Stack vs Other Data Structures

Problem TypeBest Data StructureReason
Expression EvaluationStackNatural LIFO for operations
Parentheses MatchingStackNested structure processing
Next Greater ElementMonotonic StackMaintain order efficiently
BFS TraversalQueueFIFO for level-order
DFS TraversalStack/RecursionLIFO for depth-first

Common Pitfalls

1. Stack Underflow

// Always check if stack is empty before poppingif (!st.empty()) {
int top = st.top();
st.pop();
}

2. Wrong Operand Order

// In RPN: second popped is right operandint b = st.top(); st.pop(); // Right operandint a = st.top(); st.pop(); // Left operandint result = a + b; // Correct order

3. Forgetting to Handle Edge Cases

// Check for empty stack in final resultreturn st.empty() ? 0 : st.top();

Practice Problems

Easy

Medium

Hard

Key Takeaways

  1. Stack is perfect for nested structures and expression evaluation
  2. LIFO principle naturally handles many algorithmic problems
  3. Monotonic stacks are powerful for range queries
  4. Always check for empty stack before operations
  5. Operand order matters in expression evaluation
  6. Stack can simulate recursion for iterative solutions

Related Patterns

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

History
440 lines (357 loc) · 11.6 KB

File metadata and controls

440 lines (357 loc) · 11.6 KB

Stack Pattern

Overview

The stack pattern is one of the most fundamental and widely used patterns in computer science. It follows the Last In, First Out (LIFO) principle, making it perfect for problems involving nested structures, expression evaluation, and backtracking.

When to Use Stack Pattern

1. Expression Evaluation

  • Reverse Polish Notation (RPN) evaluation
  • Infix to Postfix conversion
  • Parentheses matching and validation
  • Calculator implementations

2. Nested Structure Processing

  • Parentheses matching (valid parentheses, nested brackets)
  • HTML/XML tag matching
  • Function call tracking
  • Nested object processing

3. Backtracking and Undo Operations

  • Browser history (back button functionality)
  • Text editor undo/redo
  • Game state management
  • Recursive algorithm simulation
  • Generate Parentheses (iterative approach)

4. Monotonic Stack Problems

  • Next greater element problems
  • Largest rectangle in histogram
  • Stock span problems
  • Temperature problems

📊 Visual Guide

Monotonic Stack (Next Greater Element)

Maintains a decreasing sequence to find the next larger element.

graph TD
A[Start: Iterate nums] --> B{Stack Empty?}
B -- Yes --> C[Push Current Index]
B -- No --> D{nums[Top] < Current?}
D -- No (Keep Order) --> C
D -- Yes (Found Next Greater) --> E[Pop Top]
E --> F[Record Answer for Top]
F --> B
Loading

⚖️ Stack Variations Comparison

Stack TypeInvariant PropertyPrimary Use CaseExample
Standard LIFONoneNested structures, Undo/RedoValid Parentheses
Monotonic IncreasingElements increase bottom-to-topNext Smaller ElementLargest Rectangle Area
Monotonic DecreasingElements decrease bottom-to-topNext Greater ElementDaily Temperatures
Min/Max StackAuxiliary stack tracks min/maxO(1) Min/Max RetrievalMin Stack

Core Stack Operations

#include<stack>
#include<iostream>usingnamespacestd;voiddemonstrateStackOperations() {
stack<int> st;
// Push elements
st.push(1);
st.push(2);
st.push(3);
// Check if empty
cout << "Is empty: " << st.empty() << endl; // 0 (false)// Get size
cout << "Size: " << st.size() << endl; // 3// Access top element
cout << "Top element: " << st.top() << endl; // 3// Pop element
st.pop();
cout << "After pop, top: " << st.top() << endl; // 2// Pop all elementswhile (!st.empty()) {
cout << "Popping: " << st.top() << endl;
st.pop();
}
}

Pattern 1: Expression Evaluation

Reverse Polish Notation (RPN)

classSolution {
public:intevalRPN(vector<string>& tokens) {
stack<int> st;
for (const string& token : tokens) {
if (token == "+" || token == "-" || token == "*" || token == "/") {
int b = st.top(); st.pop();
int a = st.top(); st.pop();
if (token == "+") st.push(a + b);
elseif (token == "-") st.push(a - b);
elseif (token == "*") st.push(a * b);
elseif (token == "/") st.push(a / b);
} else {
st.push(stoi(token));
}
}
return st.top();
}
};

Key Insights for RPN

  • Operand order matters: Second popped operand is the right operand
  • Stack naturally handles the LIFO nature of operations
  • Division truncates toward zero as required

Pattern 2: Parentheses Matching

Valid Parentheses

classSolution {
public:boolisValid(string s) {
stack<char> st;
for (char c : s) {
if (c == '(' || c == '[' || c == '{') {
st.push(c);
} else {
if (st.empty()) returnfalse;
char top = st.top();
st.pop();
if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
returnfalse;
}
}
}
return st.empty();
}
};

Key Insights for Parentheses

  • Opening brackets go on stack
  • Closing brackets must match the most recent opening bracket
  • Stack empty check is crucial for valid sequences

Pattern 3: Monotonic Stack

Next Greater Element

classSolution {
public:
vector<int> nextGreaterElement(vector<int>& nums) {
int n = nums.size();
vector<int> result(n, -1);
stack<int> st; // Store indicesfor (int i = 0; i < n; i++) {
while (!st.empty() && nums[st.top()] < nums[i]) {
result[st.top()] = nums[i];
st.pop();
}
st.push(i);
}
return result;
}
};

Key Insights for Monotonic Stack

  • Maintain decreasing order in stack
  • Process elements that are smaller than current
  • Store indices for result mapping

Pattern 4: Nested Structure Processing

Simplify Path

classSolution {
public:
string simplifyPath(string path) {
stack<string> st;
stringstream ss(path);
string token;
while (getline(ss, token, '/')) {
if (token == "" || token == ".") continue;
if (token == "..") {
if (!st.empty()) st.pop();
} else {
st.push(token);
}
}
if (st.empty()) return"/";
string result = "";
while (!st.empty()) {
result = "/" + st.top() + result;
st.pop();
}
return result;
}
};

Common Stack Patterns

1. Two-Stack Approach

// Min Stack implementationclassMinStack {
private:
stack<int> data;
stack<int> minStack;
public:voidpush(int val) {
data.push(val);
if (minStack.empty() || val <= minStack.top()) {
minStack.push(val);
}
}
voidpop() {
if (data.top() == minStack.top()) {
minStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMin() { return minStack.top(); }
};

2. Stack with Additional Data

// Stack with getMax() in O(1)classMaxStack {
private:
stack<int> data;
stack<int> maxStack;
public:voidpush(int val) {
data.push(val);
if (maxStack.empty() || val >= maxStack.top()) {
maxStack.push(val);
}
}
voidpop() {
if (data.top() == maxStack.top()) {
maxStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMax() { return maxStack.top(); }
};

Advanced Stack Applications

1. Largest Rectangle in Histogram

classSolution {
public:intlargestRectangleArea(vector<int>& heights) {
stack<int> st;
int maxArea = 0;
int n = heights.size();
for (int i = 0; i <= n; i++) {
int h = (i == n) ? 0 : heights[i];
while (!st.empty() && heights[st.top()] > h) {
int height = heights[st.top()];
st.pop();
int width = st.empty() ? i : i - st.top() - 1;
maxArea = max(maxArea, height * width);
}
st.push(i);
}
return maxArea;
}
};

2. Generate Parentheses (Iterative Approach)

classSolution {
public:
vector<string> generateParenthesis(int n) {
vector<string> results;
stack<tuple<string, int, int>> st;
// Push initial state: (current_string, open_count, close_count)
st.push(make_tuple("", 0, 0));
while (!st.empty()) {
auto [current, open, close] = st.top();
st.pop();
// Base case: we've generated a complete valid combinationif (open == close && close == n) {
results.push_back(current);
continue;
}
// Add opening parenthesis if we haven't used all nif (open < n) {
st.push(make_tuple(current + "(", open + 1, close));
}
// Add closing parenthesis if we have more '(' than ')'if (close < open) {
st.push(make_tuple(current + ")", open, close + 1));
}
}
return results;
}
};

3. Trapping Rain Water

classSolution {
public:inttrap(vector<int>& height) {
stack<int> st;
int water = 0;
for (int i = 0; i < height.size(); i++) {
while (!st.empty() && height[st.top()] < height[i]) {
int top = st.top();
st.pop();
if (st.empty()) break;
int distance = i - st.top() - 1;
int boundedHeight = min(height[i], height[st.top()]) - height[top];
water += distance * boundedHeight;
}
st.push(i);
}
return water;
}
};

Stack vs Other Data Structures

Problem TypeBest Data StructureReason
Expression EvaluationStackNatural LIFO for operations
Parentheses MatchingStackNested structure processing
Next Greater ElementMonotonic StackMaintain order efficiently
BFS TraversalQueueFIFO for level-order
DFS TraversalStack/RecursionLIFO for depth-first

Common Pitfalls

1. Stack Underflow

// Always check if stack is empty before poppingif (!st.empty()) {
int top = st.top();
st.pop();
}

2. Wrong Operand Order

// In RPN: second popped is right operandint b = st.top(); st.pop(); // Right operandint a = st.top(); st.pop(); // Left operandint result = a + b; // Correct order

3. Forgetting to Handle Edge Cases

// Check for empty stack in final resultreturn st.empty() ? 0 : st.top();

Practice Problems

Easy

Medium

Hard

Key Takeaways

  1. Stack is perfect for nested structures and expression evaluation
  2. LIFO principle naturally handles many algorithmic problems
  3. Monotonic stacks are powerful for range queries
  4. Always check for empty stack before operations
  5. Operand order matters in expression evaluation
  6. Stack can simulate recursion for iterative solutions

Related Patterns

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

History
440 lines (357 loc) · 11.6 KB

File metadata and controls

440 lines (357 loc) · 11.6 KB

Stack Pattern

Overview

The stack pattern is one of the most fundamental and widely used patterns in computer science. It follows the Last In, First Out (LIFO) principle, making it perfect for problems involving nested structures, expression evaluation, and backtracking.

When to Use Stack Pattern

1. Expression Evaluation

  • Reverse Polish Notation (RPN) evaluation
  • Infix to Postfix conversion
  • Parentheses matching and validation
  • Calculator implementations

2. Nested Structure Processing

  • Parentheses matching (valid parentheses, nested brackets)
  • HTML/XML tag matching
  • Function call tracking
  • Nested object processing

3. Backtracking and Undo Operations

  • Browser history (back button functionality)
  • Text editor undo/redo
  • Game state management
  • Recursive algorithm simulation
  • Generate Parentheses (iterative approach)

4. Monotonic Stack Problems

  • Next greater element problems
  • Largest rectangle in histogram
  • Stock span problems
  • Temperature problems

📊 Visual Guide

Monotonic Stack (Next Greater Element)

Maintains a decreasing sequence to find the next larger element.

graph TD
A[Start: Iterate nums] --> B{Stack Empty?}
B -- Yes --> C[Push Current Index]
B -- No --> D{nums[Top] < Current?}
D -- No (Keep Order) --> C
D -- Yes (Found Next Greater) --> E[Pop Top]
E --> F[Record Answer for Top]
F --> B
Loading

⚖️ Stack Variations Comparison

Stack TypeInvariant PropertyPrimary Use CaseExample
Standard LIFONoneNested structures, Undo/RedoValid Parentheses
Monotonic IncreasingElements increase bottom-to-topNext Smaller ElementLargest Rectangle Area
Monotonic DecreasingElements decrease bottom-to-topNext Greater ElementDaily Temperatures
Min/Max StackAuxiliary stack tracks min/maxO(1) Min/Max RetrievalMin Stack

Core Stack Operations

#include<stack>
#include<iostream>usingnamespacestd;voiddemonstrateStackOperations() {
stack<int> st;
// Push elements
st.push(1);
st.push(2);
st.push(3);
// Check if empty
cout << "Is empty: " << st.empty() << endl; // 0 (false)// Get size
cout << "Size: " << st.size() << endl; // 3// Access top element
cout << "Top element: " << st.top() << endl; // 3// Pop element
st.pop();
cout << "After pop, top: " << st.top() << endl; // 2// Pop all elementswhile (!st.empty()) {
cout << "Popping: " << st.top() << endl;
st.pop();
}
}

Pattern 1: Expression Evaluation

Reverse Polish Notation (RPN)

classSolution {
public:intevalRPN(vector<string>& tokens) {
stack<int> st;
for (const string& token : tokens) {
if (token == "+" || token == "-" || token == "*" || token == "/") {
int b = st.top(); st.pop();
int a = st.top(); st.pop();
if (token == "+") st.push(a + b);
elseif (token == "-") st.push(a - b);
elseif (token == "*") st.push(a * b);
elseif (token == "/") st.push(a / b);
} else {
st.push(stoi(token));
}
}
return st.top();
}
};

Key Insights for RPN

  • Operand order matters: Second popped operand is the right operand
  • Stack naturally handles the LIFO nature of operations
  • Division truncates toward zero as required

Pattern 2: Parentheses Matching

Valid Parentheses

classSolution {
public:boolisValid(string s) {
stack<char> st;
for (char c : s) {
if (c == '(' || c == '[' || c == '{') {
st.push(c);
} else {
if (st.empty()) returnfalse;
char top = st.top();
st.pop();
if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
returnfalse;
}
}
}
return st.empty();
}
};

Key Insights for Parentheses

  • Opening brackets go on stack
  • Closing brackets must match the most recent opening bracket
  • Stack empty check is crucial for valid sequences

Pattern 3: Monotonic Stack

Next Greater Element

classSolution {
public:
vector<int> nextGreaterElement(vector<int>& nums) {
int n = nums.size();
vector<int> result(n, -1);
stack<int> st; // Store indicesfor (int i = 0; i < n; i++) {
while (!st.empty() && nums[st.top()] < nums[i]) {
result[st.top()] = nums[i];
st.pop();
}
st.push(i);
}
return result;
}
};

Key Insights for Monotonic Stack

  • Maintain decreasing order in stack
  • Process elements that are smaller than current
  • Store indices for result mapping

Pattern 4: Nested Structure Processing

Simplify Path

classSolution {
public:
string simplifyPath(string path) {
stack<string> st;
stringstream ss(path);
string token;
while (getline(ss, token, '/')) {
if (token == "" || token == ".") continue;
if (token == "..") {
if (!st.empty()) st.pop();
} else {
st.push(token);
}
}
if (st.empty()) return"/";
string result = "";
while (!st.empty()) {
result = "/" + st.top() + result;
st.pop();
}
return result;
}
};

Common Stack Patterns

1. Two-Stack Approach

// Min Stack implementationclassMinStack {
private:
stack<int> data;
stack<int> minStack;
public:voidpush(int val) {
data.push(val);
if (minStack.empty() || val <= minStack.top()) {
minStack.push(val);
}
}
voidpop() {
if (data.top() == minStack.top()) {
minStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMin() { return minStack.top(); }
};

2. Stack with Additional Data

// Stack with getMax() in O(1)classMaxStack {
private:
stack<int> data;
stack<int> maxStack;
public:voidpush(int val) {
data.push(val);
if (maxStack.empty() || val >= maxStack.top()) {
maxStack.push(val);
}
}
voidpop() {
if (data.top() == maxStack.top()) {
maxStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMax() { return maxStack.top(); }
};

Advanced Stack Applications

1. Largest Rectangle in Histogram

classSolution {
public:intlargestRectangleArea(vector<int>& heights) {
stack<int> st;
int maxArea = 0;
int n = heights.size();
for (int i = 0; i <= n; i++) {
int h = (i == n) ? 0 : heights[i];
while (!st.empty() && heights[st.top()] > h) {
int height = heights[st.top()];
st.pop();
int width = st.empty() ? i : i - st.top() - 1;
maxArea = max(maxArea, height * width);
}
st.push(i);
}
return maxArea;
}
};

2. Generate Parentheses (Iterative Approach)

classSolution {
public:
vector<string> generateParenthesis(int n) {
vector<string> results;
stack<tuple<string, int, int>> st;
// Push initial state: (current_string, open_count, close_count)
st.push(make_tuple("", 0, 0));
while (!st.empty()) {
auto [current, open, close] = st.top();
st.pop();
// Base case: we've generated a complete valid combinationif (open == close && close == n) {
results.push_back(current);
continue;
}
// Add opening parenthesis if we haven't used all nif (open < n) {
st.push(make_tuple(current + "(", open + 1, close));
}
// Add closing parenthesis if we have more '(' than ')'if (close < open) {
st.push(make_tuple(current + ")", open, close + 1));
}
}
return results;
}
};

3. Trapping Rain Water

classSolution {
public:inttrap(vector<int>& height) {
stack<int> st;
int water = 0;
for (int i = 0; i < height.size(); i++) {
while (!st.empty() && height[st.top()] < height[i]) {
int top = st.top();
st.pop();
if (st.empty()) break;
int distance = i - st.top() - 1;
int boundedHeight = min(height[i], height[st.top()]) - height[top];
water += distance * boundedHeight;
}
st.push(i);
}
return water;
}
};

Stack vs Other Data Structures

Problem TypeBest Data StructureReason
Expression EvaluationStackNatural LIFO for operations
Parentheses MatchingStackNested structure processing
Next Greater ElementMonotonic StackMaintain order efficiently
BFS TraversalQueueFIFO for level-order
DFS TraversalStack/RecursionLIFO for depth-first

Common Pitfalls

1. Stack Underflow

// Always check if stack is empty before poppingif (!st.empty()) {
int top = st.top();
st.pop();
}

2. Wrong Operand Order

// In RPN: second popped is right operandint b = st.top(); st.pop(); // Right operandint a = st.top(); st.pop(); // Left operandint result = a + b; // Correct order

3. Forgetting to Handle Edge Cases

// Check for empty stack in final resultreturn st.empty() ? 0 : st.top();

Practice Problems

Easy

Medium

Hard

Key Takeaways

  1. Stack is perfect for nested structures and expression evaluation
  2. LIFO principle naturally handles many algorithmic problems
  3. Monotonic stacks are powerful for range queries
  4. Always check for empty stack before operations
  5. Operand order matters in expression evaluation
  6. Stack can simulate recursion for iterative solutions

Related Patterns

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

History
440 lines (357 loc) · 11.6 KB

File metadata and controls

440 lines (357 loc) · 11.6 KB

Stack Pattern

Overview

The stack pattern is one of the most fundamental and widely used patterns in computer science. It follows the Last In, First Out (LIFO) principle, making it perfect for problems involving nested structures, expression evaluation, and backtracking.

When to Use Stack Pattern

1. Expression Evaluation

  • Reverse Polish Notation (RPN) evaluation
  • Infix to Postfix conversion
  • Parentheses matching and validation
  • Calculator implementations

2. Nested Structure Processing

  • Parentheses matching (valid parentheses, nested brackets)
  • HTML/XML tag matching
  • Function call tracking
  • Nested object processing

3. Backtracking and Undo Operations

  • Browser history (back button functionality)
  • Text editor undo/redo
  • Game state management
  • Recursive algorithm simulation
  • Generate Parentheses (iterative approach)

4. Monotonic Stack Problems

  • Next greater element problems
  • Largest rectangle in histogram
  • Stock span problems
  • Temperature problems

📊 Visual Guide

Monotonic Stack (Next Greater Element)

Maintains a decreasing sequence to find the next larger element.

graph TD
A[Start: Iterate nums] --> B{Stack Empty?}
B -- Yes --> C[Push Current Index]
B -- No --> D{nums[Top] < Current?}
D -- No (Keep Order) --> C
D -- Yes (Found Next Greater) --> E[Pop Top]
E --> F[Record Answer for Top]
F --> B
Loading

⚖️ Stack Variations Comparison

Stack TypeInvariant PropertyPrimary Use CaseExample
Standard LIFONoneNested structures, Undo/RedoValid Parentheses
Monotonic IncreasingElements increase bottom-to-topNext Smaller ElementLargest Rectangle Area
Monotonic DecreasingElements decrease bottom-to-topNext Greater ElementDaily Temperatures
Min/Max StackAuxiliary stack tracks min/maxO(1) Min/Max RetrievalMin Stack

Core Stack Operations

#include<stack>
#include<iostream>usingnamespacestd;voiddemonstrateStackOperations() {
stack<int> st;
// Push elements
st.push(1);
st.push(2);
st.push(3);
// Check if empty
cout << "Is empty: " << st.empty() << endl; // 0 (false)// Get size
cout << "Size: " << st.size() << endl; // 3// Access top element
cout << "Top element: " << st.top() << endl; // 3// Pop element
st.pop();
cout << "After pop, top: " << st.top() << endl; // 2// Pop all elementswhile (!st.empty()) {
cout << "Popping: " << st.top() << endl;
st.pop();
}
}

Pattern 1: Expression Evaluation

Reverse Polish Notation (RPN)

classSolution {
public:intevalRPN(vector<string>& tokens) {
stack<int> st;
for (const string& token : tokens) {
if (token == "+" || token == "-" || token == "*" || token == "/") {
int b = st.top(); st.pop();
int a = st.top(); st.pop();
if (token == "+") st.push(a + b);
elseif (token == "-") st.push(a - b);
elseif (token == "*") st.push(a * b);
elseif (token == "/") st.push(a / b);
} else {
st.push(stoi(token));
}
}
return st.top();
}
};

Key Insights for RPN

  • Operand order matters: Second popped operand is the right operand
  • Stack naturally handles the LIFO nature of operations
  • Division truncates toward zero as required

Pattern 2: Parentheses Matching

Valid Parentheses

classSolution {
public:boolisValid(string s) {
stack<char> st;
for (char c : s) {
if (c == '(' || c == '[' || c == '{') {
st.push(c);
} else {
if (st.empty()) returnfalse;
char top = st.top();
st.pop();
if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
returnfalse;
}
}
}
return st.empty();
}
};

Key Insights for Parentheses

  • Opening brackets go on stack
  • Closing brackets must match the most recent opening bracket
  • Stack empty check is crucial for valid sequences

Pattern 3: Monotonic Stack

Next Greater Element

classSolution {
public:
vector<int> nextGreaterElement(vector<int>& nums) {
int n = nums.size();
vector<int> result(n, -1);
stack<int> st; // Store indicesfor (int i = 0; i < n; i++) {
while (!st.empty() && nums[st.top()] < nums[i]) {
result[st.top()] = nums[i];
st.pop();
}
st.push(i);
}
return result;
}
};

Key Insights for Monotonic Stack

  • Maintain decreasing order in stack
  • Process elements that are smaller than current
  • Store indices for result mapping

Pattern 4: Nested Structure Processing

Simplify Path

classSolution {
public:
string simplifyPath(string path) {
stack<string> st;
stringstream ss(path);
string token;
while (getline(ss, token, '/')) {
if (token == "" || token == ".") continue;
if (token == "..") {
if (!st.empty()) st.pop();
} else {
st.push(token);
}
}
if (st.empty()) return"/";
string result = "";
while (!st.empty()) {
result = "/" + st.top() + result;
st.pop();
}
return result;
}
};

Common Stack Patterns

1. Two-Stack Approach

// Min Stack implementationclassMinStack {
private:
stack<int> data;
stack<int> minStack;
public:voidpush(int val) {
data.push(val);
if (minStack.empty() || val <= minStack.top()) {
minStack.push(val);
}
}
voidpop() {
if (data.top() == minStack.top()) {
minStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMin() { return minStack.top(); }
};

2. Stack with Additional Data

// Stack with getMax() in O(1)classMaxStack {
private:
stack<int> data;
stack<int> maxStack;
public:voidpush(int val) {
data.push(val);
if (maxStack.empty() || val >= maxStack.top()) {
maxStack.push(val);
}
}
voidpop() {
if (data.top() == maxStack.top()) {
maxStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMax() { return maxStack.top(); }
};

Advanced Stack Applications

1. Largest Rectangle in Histogram

classSolution {
public:intlargestRectangleArea(vector<int>& heights) {
stack<int> st;
int maxArea = 0;
int n = heights.size();
for (int i = 0; i <= n; i++) {
int h = (i == n) ? 0 : heights[i];
while (!st.empty() && heights[st.top()] > h) {
int height = heights[st.top()];
st.pop();
int width = st.empty() ? i : i - st.top() - 1;
maxArea = max(maxArea, height * width);
}
st.push(i);
}
return maxArea;
}
};

2. Generate Parentheses (Iterative Approach)

classSolution {
public:
vector<string> generateParenthesis(int n) {
vector<string> results;
stack<tuple<string, int, int>> st;
// Push initial state: (current_string, open_count, close_count)
st.push(make_tuple("", 0, 0));
while (!st.empty()) {
auto [current, open, close] = st.top();
st.pop();
// Base case: we've generated a complete valid combinationif (open == close && close == n) {
results.push_back(current);
continue;
}
// Add opening parenthesis if we haven't used all nif (open < n) {
st.push(make_tuple(current + "(", open + 1, close));
}
// Add closing parenthesis if we have more '(' than ')'if (close < open) {
st.push(make_tuple(current + ")", open, close + 1));
}
}
return results;
}
};

3. Trapping Rain Water

classSolution {
public:inttrap(vector<int>& height) {
stack<int> st;
int water = 0;
for (int i = 0; i < height.size(); i++) {
while (!st.empty() && height[st.top()] < height[i]) {
int top = st.top();
st.pop();
if (st.empty()) break;
int distance = i - st.top() - 1;
int boundedHeight = min(height[i], height[st.top()]) - height[top];
water += distance * boundedHeight;
}
st.push(i);
}
return water;
}
};

Stack vs Other Data Structures

Problem TypeBest Data StructureReason
Expression EvaluationStackNatural LIFO for operations
Parentheses MatchingStackNested structure processing
Next Greater ElementMonotonic StackMaintain order efficiently
BFS TraversalQueueFIFO for level-order
DFS TraversalStack/RecursionLIFO for depth-first

Common Pitfalls

1. Stack Underflow

// Always check if stack is empty before poppingif (!st.empty()) {
int top = st.top();
st.pop();
}

2. Wrong Operand Order

// In RPN: second popped is right operandint b = st.top(); st.pop(); // Right operandint a = st.top(); st.pop(); // Left operandint result = a + b; // Correct order

3. Forgetting to Handle Edge Cases

// Check for empty stack in final resultreturn st.empty() ? 0 : st.top();

Practice Problems

Easy

Medium

Hard

Key Takeaways

  1. Stack is perfect for nested structures and expression evaluation
  2. LIFO principle naturally handles many algorithmic problems
  3. Monotonic stacks are powerful for range queries
  4. Always check for empty stack before operations
  5. Operand order matters in expression evaluation
  6. Stack can simulate recursion for iterative solutions

Related Patterns

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

History
440 lines (357 loc) · 11.6 KB

File metadata and controls

440 lines (357 loc) · 11.6 KB

Stack Pattern

Overview

The stack pattern is one of the most fundamental and widely used patterns in computer science. It follows the Last In, First Out (LIFO) principle, making it perfect for problems involving nested structures, expression evaluation, and backtracking.

When to Use Stack Pattern

1. Expression Evaluation

  • Reverse Polish Notation (RPN) evaluation
  • Infix to Postfix conversion
  • Parentheses matching and validation
  • Calculator implementations

2. Nested Structure Processing

  • Parentheses matching (valid parentheses, nested brackets)
  • HTML/XML tag matching
  • Function call tracking
  • Nested object processing

3. Backtracking and Undo Operations

  • Browser history (back button functionality)
  • Text editor undo/redo
  • Game state management
  • Recursive algorithm simulation
  • Generate Parentheses (iterative approach)

4. Monotonic Stack Problems

  • Next greater element problems
  • Largest rectangle in histogram
  • Stock span problems
  • Temperature problems

📊 Visual Guide

Monotonic Stack (Next Greater Element)

Maintains a decreasing sequence to find the next larger element.

graph TD
A[Start: Iterate nums] --> B{Stack Empty?}
B -- Yes --> C[Push Current Index]
B -- No --> D{nums[Top] < Current?}
D -- No (Keep Order) --> C
D -- Yes (Found Next Greater) --> E[Pop Top]
E --> F[Record Answer for Top]
F --> B
Loading

⚖️ Stack Variations Comparison

Stack TypeInvariant PropertyPrimary Use CaseExample
Standard LIFONoneNested structures, Undo/RedoValid Parentheses
Monotonic IncreasingElements increase bottom-to-topNext Smaller ElementLargest Rectangle Area
Monotonic DecreasingElements decrease bottom-to-topNext Greater ElementDaily Temperatures
Min/Max StackAuxiliary stack tracks min/maxO(1) Min/Max RetrievalMin Stack

Core Stack Operations

#include<stack>
#include<iostream>usingnamespacestd;voiddemonstrateStackOperations() {
stack<int> st;
// Push elements
st.push(1);
st.push(2);
st.push(3);
// Check if empty
cout << "Is empty: " << st.empty() << endl; // 0 (false)// Get size
cout << "Size: " << st.size() << endl; // 3// Access top element
cout << "Top element: " << st.top() << endl; // 3// Pop element
st.pop();
cout << "After pop, top: " << st.top() << endl; // 2// Pop all elementswhile (!st.empty()) {
cout << "Popping: " << st.top() << endl;
st.pop();
}
}

Pattern 1: Expression Evaluation

Reverse Polish Notation (RPN)

classSolution {
public:intevalRPN(vector<string>& tokens) {
stack<int> st;
for (const string& token : tokens) {
if (token == "+" || token == "-" || token == "*" || token == "/") {
int b = st.top(); st.pop();
int a = st.top(); st.pop();
if (token == "+") st.push(a + b);
elseif (token == "-") st.push(a - b);
elseif (token == "*") st.push(a * b);
elseif (token == "/") st.push(a / b);
} else {
st.push(stoi(token));
}
}
return st.top();
}
};

Key Insights for RPN

  • Operand order matters: Second popped operand is the right operand
  • Stack naturally handles the LIFO nature of operations
  • Division truncates toward zero as required

Pattern 2: Parentheses Matching

Valid Parentheses

classSolution {
public:boolisValid(string s) {
stack<char> st;
for (char c : s) {
if (c == '(' || c == '[' || c == '{') {
st.push(c);
} else {
if (st.empty()) returnfalse;
char top = st.top();
st.pop();
if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
returnfalse;
}
}
}
return st.empty();
}
};

Key Insights for Parentheses

  • Opening brackets go on stack
  • Closing brackets must match the most recent opening bracket
  • Stack empty check is crucial for valid sequences

Pattern 3: Monotonic Stack

Next Greater Element

classSolution {
public:
vector<int> nextGreaterElement(vector<int>& nums) {
int n = nums.size();
vector<int> result(n, -1);
stack<int> st; // Store indicesfor (int i = 0; i < n; i++) {
while (!st.empty() && nums[st.top()] < nums[i]) {
result[st.top()] = nums[i];
st.pop();
}
st.push(i);
}
return result;
}
};

Key Insights for Monotonic Stack

  • Maintain decreasing order in stack
  • Process elements that are smaller than current
  • Store indices for result mapping

Pattern 4: Nested Structure Processing

Simplify Path

classSolution {
public:
string simplifyPath(string path) {
stack<string> st;
stringstream ss(path);
string token;
while (getline(ss, token, '/')) {
if (token == "" || token == ".") continue;
if (token == "..") {
if (!st.empty()) st.pop();
} else {
st.push(token);
}
}
if (st.empty()) return"/";
string result = "";
while (!st.empty()) {
result = "/" + st.top() + result;
st.pop();
}
return result;
}
};

Common Stack Patterns

1. Two-Stack Approach

// Min Stack implementationclassMinStack {
private:
stack<int> data;
stack<int> minStack;
public:voidpush(int val) {
data.push(val);
if (minStack.empty() || val <= minStack.top()) {
minStack.push(val);
}
}
voidpop() {
if (data.top() == minStack.top()) {
minStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMin() { return minStack.top(); }
};

2. Stack with Additional Data

// Stack with getMax() in O(1)classMaxStack {
private:
stack<int> data;
stack<int> maxStack;
public:voidpush(int val) {
data.push(val);
if (maxStack.empty() || val >= maxStack.top()) {
maxStack.push(val);
}
}
voidpop() {
if (data.top() == maxStack.top()) {
maxStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMax() { return maxStack.top(); }
};

Advanced Stack Applications

1. Largest Rectangle in Histogram

classSolution {
public:intlargestRectangleArea(vector<int>& heights) {
stack<int> st;
int maxArea = 0;
int n = heights.size();
for (int i = 0; i <= n; i++) {
int h = (i == n) ? 0 : heights[i];
while (!st.empty() && heights[st.top()] > h) {
int height = heights[st.top()];
st.pop();
int width = st.empty() ? i : i - st.top() - 1;
maxArea = max(maxArea, height * width);
}
st.push(i);
}
return maxArea;
}
};

2. Generate Parentheses (Iterative Approach)

classSolution {
public:
vector<string> generateParenthesis(int n) {
vector<string> results;
stack<tuple<string, int, int>> st;
// Push initial state: (current_string, open_count, close_count)
st.push(make_tuple("", 0, 0));
while (!st.empty()) {
auto [current, open, close] = st.top();
st.pop();
// Base case: we've generated a complete valid combinationif (open == close && close == n) {
results.push_back(current);
continue;
}
// Add opening parenthesis if we haven't used all nif (open < n) {
st.push(make_tuple(current + "(", open + 1, close));
}
// Add closing parenthesis if we have more '(' than ')'if (close < open) {
st.push(make_tuple(current + ")", open, close + 1));
}
}
return results;
}
};

3. Trapping Rain Water

classSolution {
public:inttrap(vector<int>& height) {
stack<int> st;
int water = 0;
for (int i = 0; i < height.size(); i++) {
while (!st.empty() && height[st.top()] < height[i]) {
int top = st.top();
st.pop();
if (st.empty()) break;
int distance = i - st.top() - 1;
int boundedHeight = min(height[i], height[st.top()]) - height[top];
water += distance * boundedHeight;
}
st.push(i);
}
return water;
}
};

Stack vs Other Data Structures

Problem TypeBest Data StructureReason
Expression EvaluationStackNatural LIFO for operations
Parentheses MatchingStackNested structure processing
Next Greater ElementMonotonic StackMaintain order efficiently
BFS TraversalQueueFIFO for level-order
DFS TraversalStack/RecursionLIFO for depth-first

Common Pitfalls

1. Stack Underflow

// Always check if stack is empty before poppingif (!st.empty()) {
int top = st.top();
st.pop();
}

2. Wrong Operand Order

// In RPN: second popped is right operandint b = st.top(); st.pop(); // Right operandint a = st.top(); st.pop(); // Left operandint result = a + b; // Correct order

3. Forgetting to Handle Edge Cases

// Check for empty stack in final resultreturn st.empty() ? 0 : st.top();

Practice Problems

Easy

Medium

Hard

Key Takeaways

  1. Stack is perfect for nested structures and expression evaluation
  2. LIFO principle naturally handles many algorithmic problems
  3. Monotonic stacks are powerful for range queries
  4. Always check for empty stack before operations
  5. Operand order matters in expression evaluation
  6. Stack can simulate recursion for iterative solutions

Related Patterns

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

History
440 lines (357 loc) · 11.6 KB

File metadata and controls

440 lines (357 loc) · 11.6 KB

Stack Pattern

Overview

The stack pattern is one of the most fundamental and widely used patterns in computer science. It follows the Last In, First Out (LIFO) principle, making it perfect for problems involving nested structures, expression evaluation, and backtracking.

When to Use Stack Pattern

1. Expression Evaluation

  • Reverse Polish Notation (RPN) evaluation
  • Infix to Postfix conversion
  • Parentheses matching and validation
  • Calculator implementations

2. Nested Structure Processing

  • Parentheses matching (valid parentheses, nested brackets)
  • HTML/XML tag matching
  • Function call tracking
  • Nested object processing

3. Backtracking and Undo Operations

  • Browser history (back button functionality)
  • Text editor undo/redo
  • Game state management
  • Recursive algorithm simulation
  • Generate Parentheses (iterative approach)

4. Monotonic Stack Problems

  • Next greater element problems
  • Largest rectangle in histogram
  • Stock span problems
  • Temperature problems

📊 Visual Guide

Monotonic Stack (Next Greater Element)

Maintains a decreasing sequence to find the next larger element.

graph TD
A[Start: Iterate nums] --> B{Stack Empty?}
B -- Yes --> C[Push Current Index]
B -- No --> D{nums[Top] < Current?}
D -- No (Keep Order) --> C
D -- Yes (Found Next Greater) --> E[Pop Top]
E --> F[Record Answer for Top]
F --> B
Loading

⚖️ Stack Variations Comparison

Stack TypeInvariant PropertyPrimary Use CaseExample
Standard LIFONoneNested structures, Undo/RedoValid Parentheses
Monotonic IncreasingElements increase bottom-to-topNext Smaller ElementLargest Rectangle Area
Monotonic DecreasingElements decrease bottom-to-topNext Greater ElementDaily Temperatures
Min/Max StackAuxiliary stack tracks min/maxO(1) Min/Max RetrievalMin Stack

Core Stack Operations

#include<stack>
#include<iostream>usingnamespacestd;voiddemonstrateStackOperations() {
stack<int> st;
// Push elements
st.push(1);
st.push(2);
st.push(3);
// Check if empty
cout << "Is empty: " << st.empty() << endl; // 0 (false)// Get size
cout << "Size: " << st.size() << endl; // 3// Access top element
cout << "Top element: " << st.top() << endl; // 3// Pop element
st.pop();
cout << "After pop, top: " << st.top() << endl; // 2// Pop all elementswhile (!st.empty()) {
cout << "Popping: " << st.top() << endl;
st.pop();
}
}

Pattern 1: Expression Evaluation

Reverse Polish Notation (RPN)

classSolution {
public:intevalRPN(vector<string>& tokens) {
stack<int> st;
for (const string& token : tokens) {
if (token == "+" || token == "-" || token == "*" || token == "/") {
int b = st.top(); st.pop();
int a = st.top(); st.pop();
if (token == "+") st.push(a + b);
elseif (token == "-") st.push(a - b);
elseif (token == "*") st.push(a * b);
elseif (token == "/") st.push(a / b);
} else {
st.push(stoi(token));
}
}
return st.top();
}
};

Key Insights for RPN

  • Operand order matters: Second popped operand is the right operand
  • Stack naturally handles the LIFO nature of operations
  • Division truncates toward zero as required

Pattern 2: Parentheses Matching

Valid Parentheses

classSolution {
public:boolisValid(string s) {
stack<char> st;
for (char c : s) {
if (c == '(' || c == '[' || c == '{') {
st.push(c);
} else {
if (st.empty()) returnfalse;
char top = st.top();
st.pop();
if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
returnfalse;
}
}
}
return st.empty();
}
};

Key Insights for Parentheses

  • Opening brackets go on stack
  • Closing brackets must match the most recent opening bracket
  • Stack empty check is crucial for valid sequences

Pattern 3: Monotonic Stack

Next Greater Element

classSolution {
public:
vector<int> nextGreaterElement(vector<int>& nums) {
int n = nums.size();
vector<int> result(n, -1);
stack<int> st; // Store indicesfor (int i = 0; i < n; i++) {
while (!st.empty() && nums[st.top()] < nums[i]) {
result[st.top()] = nums[i];
st.pop();
}
st.push(i);
}
return result;
}
};

Key Insights for Monotonic Stack

  • Maintain decreasing order in stack
  • Process elements that are smaller than current
  • Store indices for result mapping

Pattern 4: Nested Structure Processing

Simplify Path

classSolution {
public:
string simplifyPath(string path) {
stack<string> st;
stringstream ss(path);
string token;
while (getline(ss, token, '/')) {
if (token == "" || token == ".") continue;
if (token == "..") {
if (!st.empty()) st.pop();
} else {
st.push(token);
}
}
if (st.empty()) return"/";
string result = "";
while (!st.empty()) {
result = "/" + st.top() + result;
st.pop();
}
return result;
}
};

Common Stack Patterns

1. Two-Stack Approach

// Min Stack implementationclassMinStack {
private:
stack<int> data;
stack<int> minStack;
public:voidpush(int val) {
data.push(val);
if (minStack.empty() || val <= minStack.top()) {
minStack.push(val);
}
}
voidpop() {
if (data.top() == minStack.top()) {
minStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMin() { return minStack.top(); }
};

2. Stack with Additional Data

// Stack with getMax() in O(1)classMaxStack {
private:
stack<int> data;
stack<int> maxStack;
public:voidpush(int val) {
data.push(val);
if (maxStack.empty() || val >= maxStack.top()) {
maxStack.push(val);
}
}
voidpop() {
if (data.top() == maxStack.top()) {
maxStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMax() { return maxStack.top(); }
};

Advanced Stack Applications

1. Largest Rectangle in Histogram

classSolution {
public:intlargestRectangleArea(vector<int>& heights) {
stack<int> st;
int maxArea = 0;
int n = heights.size();
for (int i = 0; i <= n; i++) {
int h = (i == n) ? 0 : heights[i];
while (!st.empty() && heights[st.top()] > h) {
int height = heights[st.top()];
st.pop();
int width = st.empty() ? i : i - st.top() - 1;
maxArea = max(maxArea, height * width);
}
st.push(i);
}
return maxArea;
}
};

2. Generate Parentheses (Iterative Approach)

classSolution {
public:
vector<string> generateParenthesis(int n) {
vector<string> results;
stack<tuple<string, int, int>> st;
// Push initial state: (current_string, open_count, close_count)
st.push(make_tuple("", 0, 0));
while (!st.empty()) {
auto [current, open, close] = st.top();
st.pop();
// Base case: we've generated a complete valid combinationif (open == close && close == n) {
results.push_back(current);
continue;
}
// Add opening parenthesis if we haven't used all nif (open < n) {
st.push(make_tuple(current + "(", open + 1, close));
}
// Add closing parenthesis if we have more '(' than ')'if (close < open) {
st.push(make_tuple(current + ")", open, close + 1));
}
}
return results;
}
};

3. Trapping Rain Water

classSolution {
public:inttrap(vector<int>& height) {
stack<int> st;
int water = 0;
for (int i = 0; i < height.size(); i++) {
while (!st.empty() && height[st.top()] < height[i]) {
int top = st.top();
st.pop();
if (st.empty()) break;
int distance = i - st.top() - 1;
int boundedHeight = min(height[i], height[st.top()]) - height[top];
water += distance * boundedHeight;
}
st.push(i);
}
return water;
}
};

Stack vs Other Data Structures

Problem TypeBest Data StructureReason
Expression EvaluationStackNatural LIFO for operations
Parentheses MatchingStackNested structure processing
Next Greater ElementMonotonic StackMaintain order efficiently
BFS TraversalQueueFIFO for level-order
DFS TraversalStack/RecursionLIFO for depth-first

Common Pitfalls

1. Stack Underflow

// Always check if stack is empty before poppingif (!st.empty()) {
int top = st.top();
st.pop();
}

2. Wrong Operand Order

// In RPN: second popped is right operandint b = st.top(); st.pop(); // Right operandint a = st.top(); st.pop(); // Left operandint result = a + b; // Correct order

3. Forgetting to Handle Edge Cases

// Check for empty stack in final resultreturn st.empty() ? 0 : st.top();

Practice Problems

Easy

Medium

Hard

Key Takeaways

  1. Stack is perfect for nested structures and expression evaluation
  2. LIFO principle naturally handles many algorithmic problems
  3. Monotonic stacks are powerful for range queries
  4. Always check for empty stack before operations
  5. Operand order matters in expression evaluation
  6. Stack can simulate recursion for iterative solutions

Related Patterns

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

History
440 lines (357 loc) · 11.6 KB

File metadata and controls

440 lines (357 loc) · 11.6 KB

Stack Pattern

Overview

The stack pattern is one of the most fundamental and widely used patterns in computer science. It follows the Last In, First Out (LIFO) principle, making it perfect for problems involving nested structures, expression evaluation, and backtracking.

When to Use Stack Pattern

1. Expression Evaluation

  • Reverse Polish Notation (RPN) evaluation
  • Infix to Postfix conversion
  • Parentheses matching and validation
  • Calculator implementations

2. Nested Structure Processing

  • Parentheses matching (valid parentheses, nested brackets)
  • HTML/XML tag matching
  • Function call tracking
  • Nested object processing

3. Backtracking and Undo Operations

  • Browser history (back button functionality)
  • Text editor undo/redo
  • Game state management
  • Recursive algorithm simulation
  • Generate Parentheses (iterative approach)

4. Monotonic Stack Problems

  • Next greater element problems
  • Largest rectangle in histogram
  • Stock span problems
  • Temperature problems

📊 Visual Guide

Monotonic Stack (Next Greater Element)

Maintains a decreasing sequence to find the next larger element.

graph TD
A[Start: Iterate nums] --> B{Stack Empty?}
B -- Yes --> C[Push Current Index]
B -- No --> D{nums[Top] < Current?}
D -- No (Keep Order) --> C
D -- Yes (Found Next Greater) --> E[Pop Top]
E --> F[Record Answer for Top]
F --> B
Loading

⚖️ Stack Variations Comparison

Stack TypeInvariant PropertyPrimary Use CaseExample
Standard LIFONoneNested structures, Undo/RedoValid Parentheses
Monotonic IncreasingElements increase bottom-to-topNext Smaller ElementLargest Rectangle Area
Monotonic DecreasingElements decrease bottom-to-topNext Greater ElementDaily Temperatures
Min/Max StackAuxiliary stack tracks min/maxO(1) Min/Max RetrievalMin Stack

Core Stack Operations

#include<stack>
#include<iostream>usingnamespacestd;voiddemonstrateStackOperations() {
stack<int> st;
// Push elements
st.push(1);
st.push(2);
st.push(3);
// Check if empty
cout << "Is empty: " << st.empty() << endl; // 0 (false)// Get size
cout << "Size: " << st.size() << endl; // 3// Access top element
cout << "Top element: " << st.top() << endl; // 3// Pop element
st.pop();
cout << "After pop, top: " << st.top() << endl; // 2// Pop all elementswhile (!st.empty()) {
cout << "Popping: " << st.top() << endl;
st.pop();
}
}

Pattern 1: Expression Evaluation

Reverse Polish Notation (RPN)

classSolution {
public:intevalRPN(vector<string>& tokens) {
stack<int> st;
for (const string& token : tokens) {
if (token == "+" || token == "-" || token == "*" || token == "/") {
int b = st.top(); st.pop();
int a = st.top(); st.pop();
if (token == "+") st.push(a + b);
elseif (token == "-") st.push(a - b);
elseif (token == "*") st.push(a * b);
elseif (token == "/") st.push(a / b);
} else {
st.push(stoi(token));
}
}
return st.top();
}
};

Key Insights for RPN

  • Operand order matters: Second popped operand is the right operand
  • Stack naturally handles the LIFO nature of operations
  • Division truncates toward zero as required

Pattern 2: Parentheses Matching

Valid Parentheses

classSolution {
public:boolisValid(string s) {
stack<char> st;
for (char c : s) {
if (c == '(' || c == '[' || c == '{') {
st.push(c);
} else {
if (st.empty()) returnfalse;
char top = st.top();
st.pop();
if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
returnfalse;
}
}
}
return st.empty();
}
};

Key Insights for Parentheses

  • Opening brackets go on stack
  • Closing brackets must match the most recent opening bracket
  • Stack empty check is crucial for valid sequences

Pattern 3: Monotonic Stack

Next Greater Element

classSolution {
public:
vector<int> nextGreaterElement(vector<int>& nums) {
int n = nums.size();
vector<int> result(n, -1);
stack<int> st; // Store indicesfor (int i = 0; i < n; i++) {
while (!st.empty() && nums[st.top()] < nums[i]) {
result[st.top()] = nums[i];
st.pop();
}
st.push(i);
}
return result;
}
};

Key Insights for Monotonic Stack

  • Maintain decreasing order in stack
  • Process elements that are smaller than current
  • Store indices for result mapping

Pattern 4: Nested Structure Processing

Simplify Path

classSolution {
public:
string simplifyPath(string path) {
stack<string> st;
stringstream ss(path);
string token;
while (getline(ss, token, '/')) {
if (token == "" || token == ".") continue;
if (token == "..") {
if (!st.empty()) st.pop();
} else {
st.push(token);
}
}
if (st.empty()) return"/";
string result = "";
while (!st.empty()) {
result = "/" + st.top() + result;
st.pop();
}
return result;
}
};

Common Stack Patterns

1. Two-Stack Approach

// Min Stack implementationclassMinStack {
private:
stack<int> data;
stack<int> minStack;
public:voidpush(int val) {
data.push(val);
if (minStack.empty() || val <= minStack.top()) {
minStack.push(val);
}
}
voidpop() {
if (data.top() == minStack.top()) {
minStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMin() { return minStack.top(); }
};

2. Stack with Additional Data

// Stack with getMax() in O(1)classMaxStack {
private:
stack<int> data;
stack<int> maxStack;
public:voidpush(int val) {
data.push(val);
if (maxStack.empty() || val >= maxStack.top()) {
maxStack.push(val);
}
}
voidpop() {
if (data.top() == maxStack.top()) {
maxStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMax() { return maxStack.top(); }
};

Advanced Stack Applications

1. Largest Rectangle in Histogram

classSolution {
public:intlargestRectangleArea(vector<int>& heights) {
stack<int> st;
int maxArea = 0;
int n = heights.size();
for (int i = 0; i <= n; i++) {
int h = (i == n) ? 0 : heights[i];
while (!st.empty() && heights[st.top()] > h) {
int height = heights[st.top()];
st.pop();
int width = st.empty() ? i : i - st.top() - 1;
maxArea = max(maxArea, height * width);
}
st.push(i);
}
return maxArea;
}
};

2. Generate Parentheses (Iterative Approach)

classSolution {
public:
vector<string> generateParenthesis(int n) {
vector<string> results;
stack<tuple<string, int, int>> st;
// Push initial state: (current_string, open_count, close_count)
st.push(make_tuple("", 0, 0));
while (!st.empty()) {
auto [current, open, close] = st.top();
st.pop();
// Base case: we've generated a complete valid combinationif (open == close && close == n) {
results.push_back(current);
continue;
}
// Add opening parenthesis if we haven't used all nif (open < n) {
st.push(make_tuple(current + "(", open + 1, close));
}
// Add closing parenthesis if we have more '(' than ')'if (close < open) {
st.push(make_tuple(current + ")", open, close + 1));
}
}
return results;
}
};

3. Trapping Rain Water

classSolution {
public:inttrap(vector<int>& height) {
stack<int> st;
int water = 0;
for (int i = 0; i < height.size(); i++) {
while (!st.empty() && height[st.top()] < height[i]) {
int top = st.top();
st.pop();
if (st.empty()) break;
int distance = i - st.top() - 1;
int boundedHeight = min(height[i], height[st.top()]) - height[top];
water += distance * boundedHeight;
}
st.push(i);
}
return water;
}
};

Stack vs Other Data Structures

Problem TypeBest Data StructureReason
Expression EvaluationStackNatural LIFO for operations
Parentheses MatchingStackNested structure processing
Next Greater ElementMonotonic StackMaintain order efficiently
BFS TraversalQueueFIFO for level-order
DFS TraversalStack/RecursionLIFO for depth-first

Common Pitfalls

1. Stack Underflow

// Always check if stack is empty before poppingif (!st.empty()) {
int top = st.top();
st.pop();
}

2. Wrong Operand Order

// In RPN: second popped is right operandint b = st.top(); st.pop(); // Right operandint a = st.top(); st.pop(); // Left operandint result = a + b; // Correct order

3. Forgetting to Handle Edge Cases

// Check for empty stack in final resultreturn st.empty() ? 0 : st.top();

Practice Problems

Easy

Medium

Hard

Key Takeaways

  1. Stack is perfect for nested structures and expression evaluation
  2. LIFO principle naturally handles many algorithmic problems
  3. Monotonic stacks are powerful for range queries
  4. Always check for empty stack before operations
  5. Operand order matters in expression evaluation
  6. Stack can simulate recursion for iterative solutions

Related Patterns

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

History
440 lines (357 loc) · 11.6 KB

File metadata and controls

440 lines (357 loc) · 11.6 KB

Stack Pattern

Overview

The stack pattern is one of the most fundamental and widely used patterns in computer science. It follows the Last In, First Out (LIFO) principle, making it perfect for problems involving nested structures, expression evaluation, and backtracking.

When to Use Stack Pattern

1. Expression Evaluation

  • Reverse Polish Notation (RPN) evaluation
  • Infix to Postfix conversion
  • Parentheses matching and validation
  • Calculator implementations

2. Nested Structure Processing

  • Parentheses matching (valid parentheses, nested brackets)
  • HTML/XML tag matching
  • Function call tracking
  • Nested object processing

3. Backtracking and Undo Operations

  • Browser history (back button functionality)
  • Text editor undo/redo
  • Game state management
  • Recursive algorithm simulation
  • Generate Parentheses (iterative approach)

4. Monotonic Stack Problems

  • Next greater element problems
  • Largest rectangle in histogram
  • Stock span problems
  • Temperature problems

📊 Visual Guide

Monotonic Stack (Next Greater Element)

Maintains a decreasing sequence to find the next larger element.

graph TD
A[Start: Iterate nums] --> B{Stack Empty?}
B -- Yes --> C[Push Current Index]
B -- No --> D{nums[Top] < Current?}
D -- No (Keep Order) --> C
D -- Yes (Found Next Greater) --> E[Pop Top]
E --> F[Record Answer for Top]
F --> B
Loading

⚖️ Stack Variations Comparison

Stack TypeInvariant PropertyPrimary Use CaseExample
Standard LIFONoneNested structures, Undo/RedoValid Parentheses
Monotonic IncreasingElements increase bottom-to-topNext Smaller ElementLargest Rectangle Area
Monotonic DecreasingElements decrease bottom-to-topNext Greater ElementDaily Temperatures
Min/Max StackAuxiliary stack tracks min/maxO(1) Min/Max RetrievalMin Stack

Core Stack Operations

#include<stack>
#include<iostream>usingnamespacestd;voiddemonstrateStackOperations() {
stack<int> st;
// Push elements
st.push(1);
st.push(2);
st.push(3);
// Check if empty
cout << "Is empty: " << st.empty() << endl; // 0 (false)// Get size
cout << "Size: " << st.size() << endl; // 3// Access top element
cout << "Top element: " << st.top() << endl; // 3// Pop element
st.pop();
cout << "After pop, top: " << st.top() << endl; // 2// Pop all elementswhile (!st.empty()) {
cout << "Popping: " << st.top() << endl;
st.pop();
}
}

Pattern 1: Expression Evaluation

Reverse Polish Notation (RPN)

classSolution {
public:intevalRPN(vector<string>& tokens) {
stack<int> st;
for (const string& token : tokens) {
if (token == "+" || token == "-" || token == "*" || token == "/") {
int b = st.top(); st.pop();
int a = st.top(); st.pop();
if (token == "+") st.push(a + b);
elseif (token == "-") st.push(a - b);
elseif (token == "*") st.push(a * b);
elseif (token == "/") st.push(a / b);
} else {
st.push(stoi(token));
}
}
return st.top();
}
};

Key Insights for RPN

  • Operand order matters: Second popped operand is the right operand
  • Stack naturally handles the LIFO nature of operations
  • Division truncates toward zero as required

Pattern 2: Parentheses Matching

Valid Parentheses

classSolution {
public:boolisValid(string s) {
stack<char> st;
for (char c : s) {
if (c == '(' || c == '[' || c == '{') {
st.push(c);
} else {
if (st.empty()) returnfalse;
char top = st.top();
st.pop();
if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
returnfalse;
}
}
}
return st.empty();
}
};

Key Insights for Parentheses

  • Opening brackets go on stack
  • Closing brackets must match the most recent opening bracket
  • Stack empty check is crucial for valid sequences

Pattern 3: Monotonic Stack

Next Greater Element

classSolution {
public:
vector<int> nextGreaterElement(vector<int>& nums) {
int n = nums.size();
vector<int> result(n, -1);
stack<int> st; // Store indicesfor (int i = 0; i < n; i++) {
while (!st.empty() && nums[st.top()] < nums[i]) {
result[st.top()] = nums[i];
st.pop();
}
st.push(i);
}
return result;
}
};

Key Insights for Monotonic Stack

  • Maintain decreasing order in stack
  • Process elements that are smaller than current
  • Store indices for result mapping

Pattern 4: Nested Structure Processing

Simplify Path

classSolution {
public:
string simplifyPath(string path) {
stack<string> st;
stringstream ss(path);
string token;
while (getline(ss, token, '/')) {
if (token == "" || token == ".") continue;
if (token == "..") {
if (!st.empty()) st.pop();
} else {
st.push(token);
}
}
if (st.empty()) return"/";
string result = "";
while (!st.empty()) {
result = "/" + st.top() + result;
st.pop();
}
return result;
}
};

Common Stack Patterns

1. Two-Stack Approach

// Min Stack implementationclassMinStack {
private:
stack<int> data;
stack<int> minStack;
public:voidpush(int val) {
data.push(val);
if (minStack.empty() || val <= minStack.top()) {
minStack.push(val);
}
}
voidpop() {
if (data.top() == minStack.top()) {
minStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMin() { return minStack.top(); }
};

2. Stack with Additional Data

// Stack with getMax() in O(1)classMaxStack {
private:
stack<int> data;
stack<int> maxStack;
public:voidpush(int val) {
data.push(val);
if (maxStack.empty() || val >= maxStack.top()) {
maxStack.push(val);
}
}
voidpop() {
if (data.top() == maxStack.top()) {
maxStack.pop();
}
data.pop();
}
inttop() { return data.top(); }
intgetMax() { return maxStack.top(); }
};

Advanced Stack Applications

1. Largest Rectangle in Histogram

classSolution {
public:intlargestRectangleArea(vector<int>& heights) {
stack<int> st;
int maxArea = 0;
int n = heights.size();
for (int i = 0; i <= n; i++) {
int h = (i == n) ? 0 : heights[i];
while (!st.empty() && heights[st.top()] > h) {
int height = heights[st.top()];
st.pop();
int width = st.empty() ? i : i - st.top() - 1;
maxArea = max(maxArea, height * width);
}
st.push(i);
}
return maxArea;
}
};

2. Generate Parentheses (Iterative Approach)

classSolution {
public:
vector<string> generateParenthesis(int n) {
vector<string> results;
stack<tuple<string, int, int>> st;
// Push initial state: (current_string, open_count, close_count)
st.push(make_tuple("", 0, 0));
while (!st.empty()) {
auto [current, open, close] = st.top();
st.pop();
// Base case: we've generated a complete valid combinationif (open == close && close == n) {
results.push_back(current);
continue;
}
// Add opening parenthesis if we haven't used all nif (open < n) {
st.push(make_tuple(current + "(", open + 1, close));
}
// Add closing parenthesis if we have more '(' than ')'if (close < open) {
st.push(make_tuple(current + ")", open, close + 1));
}
}
return results;
}
};

3. Trapping Rain Water

classSolution {
public:inttrap(vector<int>& height) {
stack<int> st;
int water = 0;
for (int i = 0; i < height.size(); i++) {
while (!st.empty() && height[st.top()] < height[i]) {
int top = st.top();
st.pop();
if (st.empty()) break;
int distance = i - st.top() - 1;
int boundedHeight = min(height[i], height[st.top()]) - height[top];
water += distance * boundedHeight;
}
st.push(i);
}
return water;
}
};

Stack vs Other Data Structures

Problem TypeBest Data StructureReason
Expression EvaluationStackNatural LIFO for operations
Parentheses MatchingStackNested structure processing
Next Greater ElementMonotonic StackMaintain order efficiently
BFS TraversalQueueFIFO for level-order
DFS TraversalStack/RecursionLIFO for depth-first

Common Pitfalls

1. Stack Underflow

// Always check if stack is empty before poppingif (!st.empty()) {
int top = st.top();
st.pop();
}

2. Wrong Operand Order

// In RPN: second popped is right operandint b = st.top(); st.pop(); // Right operandint a = st.top(); st.pop(); // Left operandint result = a + b; // Correct order

3. Forgetting to Handle Edge Cases

// Check for empty stack in final resultreturn st.empty() ? 0 : st.top();

Practice Problems

Easy

Medium

Hard

Key Takeaways

  1. Stack is perfect for nested structures and expression evaluation
  2. LIFO principle naturally handles many algorithmic problems
  3. Monotonic stacks are powerful for range queries
  4. Always check for empty stack before operations
  5. Operand order matters in expression evaluation
  6. Stack can simulate recursion for iterative solutions

Related Patterns