Report Content
×Stacks in DSA: LIFO Principles & Applications
Master stack operations, implementation variants, and real-world applications. This tutorial covers parentheses matching, expression evaluation, and common interview problems with coding examples.
Stack Usage in Systems (2023)
1. Stack Fundamentals
Stack (LIFO Data Structure)
Definition
Stack is a linear data structure that follows the Last In First Out (LIFO) principle. Elements are added (pushed) and removed (popped) from the same end called the top. Stacks are used extensively in function call management, expression evaluation, and backtracking algorithms.
How It Works
Push Operation
- Add element to the top of stack
- Update top pointer/reference
- O(1) time complexity
Pop Operation
- Remove and return top element
- Check for underflow (empty stack)
- O(1) time complexity
Peek/Top Operation
- Return top element without removing
- Useful for inspection
- O(1) time complexity
Array-based Stack Implementation
class ArrayStack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
if self.is_empty():
raise IndexError("Stack underflow")
return self.items.pop()
def peek(self):
if self.is_empty():
return None
return self.items[-1]
def is_empty(self):
return len(self.items) == 0
def size(self):
return len(self.items)
Linked List-based Stack Implementation
class ListNode:
def __init__(self, val, next_node=None):
self.val = val
self.next = next_node
class LinkedListStack:
def __init__(self):
self.top = None
def push(self, val):
self.top = ListNode(val, self.top)
def pop(self):
if self.is_empty():
raise IndexError("Stack underflow")
val = self.top.val
self.top = self.top.next
return val
def peek(self):
if self.is_empty():
return None
return self.top.val
def is_empty(self):
return self.top is None
Complexity: Push O(1), Pop O(1), Peek O(1)
Space: O(n) for n elements
Use When: LIFO access pattern needed
2. Valid Parentheses
Parentheses Matching
Definition
Valid Parentheses checks if a string of brackets is properly matched and nested. Each opening bracket must have a corresponding closing bracket of the same type in the correct order.
How It Works
Initialize stack
- Create empty stack
- Define matching pairs
Process each character
- If opening bracket, push to stack
- If closing bracket, check if matches top
- If mismatch or stack empty → invalid
Verify complete matching
- After processing all characters
- Stack must be empty for valid string
def is_valid_parentheses(s: str) -> bool:
stack = []
pairs = {
'(': ')',
'[': ']',
'{': '}'
}
for char in s:
if char in pairs: # Opening bracket
stack.append(char)
else: # Closing bracket
if not stack or pairs[stack.pop()] != char:
return False
return len(stack) == 0
# Test cases
print(is_valid_parentheses("()")) # True
print(is_valid_parentheses("()[]{}")) # True
print(is_valid_parentheses("(]")) # False
print(is_valid_parentheses("([)]")) # False
print(is_valid_parentheses("{[]}")) # True
Complexity: O(n) time, O(n) space
Use When: Validating expressions, code parsing
3. Postfix Expression Evaluation
Postfix (Reverse Polish Notation) Evaluation
Definition
Postfix Evaluation computes the result of an expression where operators follow their operands (e.g., "3 4 +" = 7). Stacks naturally evaluate RPN without needing parentheses.
How It Works
Initialize stack
- Create empty stack for operands
- Split expression into tokens
Process each token
- If number, push to stack
- If operator, pop two operands
- Apply operation, push result
Return final result
- Stack contains single value
- That value is the expression result
def evaluate_postfix(expression):
stack = []
operators = {
'+': lambda a, b: a + b,
'-': lambda a, b: a - b,
'*': lambda a, b: a * b,
'/': lambda a, b: int(a / b), # Integer division
'^': lambda a, b: a ** b
}
for token in expression.split():
if token in operators:
b = stack.pop()
a = stack.pop()
result = operators[token](a, b)
stack.append(result)
else:
stack.append(int(token))
return stack.pop()
# Example
expr = "3 4 + 2 * 7 /" # (3+4)*2/7 = 2
print(evaluate_postfix(expr)) # 2
expr2 = "5 1 2 + 4 * + 3 -" # 5+((1+2)*4)-3 = 14
print(evaluate_postfix(expr2)) # 14
Complexity: O(n) time, O(n) space
Use When: Calculator implementation, expression evaluation
4. Min Stack (Constant Time Minimum)
Min Stack with O(1) Get Minimum
Definition
Min Stack is a stack that supports push, pop, top, and getMin operations all in O(1) time. It uses an auxiliary stack to track minimum values at each state.
How It Works
Maintain two stacks
- Main stack stores all elements
- Min stack stores current minimums
Push operation
- Push value to main stack
- If min stack empty or value ≤ current min, push to min stack
Pop operation
- Pop from main stack
- If popped value equals min stack top, pop from min stack
class MinStack:
def __init__(self):
self.stack = []
self.min_stack = []
def push(self, val):
self.stack.append(val)
if not self.min_stack or val <= self.min_stack[-1]:
self.min_stack.append(val)
def pop(self):
if not self.stack:
return None
val = self.stack.pop()
if val == self.min_stack[-1]:
self.min_stack.pop()
return val
def top(self):
return self.stack[-1] if self.stack else None
def get_min(self):
return self.min_stack[-1] if self.min_stack else None
# Example usage
min_stack = MinStack()
min_stack.push(5)
min_stack.push(2)
min_stack.push(7)
min_stack.push(1)
print(min_stack.get_min()) # 1
min_stack.pop() # removes 1
print(min_stack.get_min()) # 2
Complexity: All operations O(1) time, O(n) space
Use When: Need constant time minimum retrieval
5. Monotonic Stack (Next Greater Element)
Next Greater Element Using Monotonic Stack
Definition
Monotonic Stack maintains elements in increasing or decreasing order. It's useful for finding next greater/smaller elements efficiently in O(n) time.
How It Works
Initialize empty stack
- Stack stores indices of elements
- Results array initialized with -1
Process each element
- While stack not empty and current > element at stack top
- Pop index and record current as next greater
Push current index to stack
- Remaining indices have no greater element
- Result holds -1 for those
def next_greater_elements(nums):
result = [-1] * len(nums)
stack = []
for i in range(len(nums)):
while stack and nums[i] > nums[stack[-1]]:
idx = stack.pop()
result[idx] = nums[i]
stack.append(i)
return result
def next_smaller_elements(nums):
result = [-1] * len(nums)
stack = []
for i in range(len(nums)):
while stack and nums[i] < nums[stack[-1]]:
idx = stack.pop()
result[idx] = nums[i]
stack.append(i)
return result
# Example
nums = [4, 2, 9, 7, 5, 3, 6]
print(next_greater_elements(nums)) # [9, 9, -1, -1, 6, 6, -1]
print(next_smaller_elements(nums)) # [2, -1, 7, 5, 3, -1, -1]
Complexity: O(n) time, O(n) space
Use When: Next greater/smaller element problems
6. Queue Using Two Stacks
Implement Queue with Stacks
Definition
Queue Using Stacks implements First In First Out (FIFO) behavior using two LIFO stacks. One stack handles enqueue operations, the other handles dequeue operations.
How It Works
Maintain two stacks
- inputStack: handles push/enqueue
- outputStack: handles pop/dequeue
Enqueue operation
- Simply push to inputStack
- O(1) time
Dequeue operation
- If outputStack empty, transfer all from inputStack
- Pop from outputStack
- Amortized O(1) time
class QueueUsingStacks:
def __init__(self):
self.input_stack = []
self.output_stack = []
def enqueue(self, val):
self.input_stack.append(val)
def dequeue(self):
if not self.output_stack:
while self.input_stack:
self.output_stack.append(self.input_stack.pop())
if not self.output_stack:
raise IndexError("Queue is empty")
return self.output_stack.pop()
def peek(self):
if not self.output_stack:
while self.input_stack:
self.output_stack.append(self.input_stack.pop())
if not self.output_stack:
return None
return self.output_stack[-1]
def is_empty(self):
return len(self.input_stack) == 0 and len(self.output_stack) == 0
# Example usage
q = QueueUsingStacks()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
print(q.dequeue()) # 1
print(q.dequeue()) # 2
q.enqueue(4)
print(q.dequeue()) # 3
print(q.dequeue()) # 4
Complexity: Enqueue O(1), Dequeue amortized O(1)
Use When: Need queue behavior with limited stack implementations
Stack Problem-Solving Cheat Sheet
| Problem Type | Approach | Time Complexity |
|---|---|---|
| Parentheses Validation | Push opens, pop on closes | O(n) |
| Next Greater Element | Monotonic decreasing stack | O(n) |
| Largest Rectangle in Histogram | Monotonic stack with indices | O(n) |
| Infix to Postfix Conversion | Operator precedence stack | O(n) |
| Validating Stack Sequences | Simulate with single stack | O(n) |
| Min Stack (constant time minimum) | Auxiliary min stack | O(1) per op |
Stack Implementation Checklist
7. Real-World Applications
Browser History (Back/Forward)
Two-stack approach: Manage navigation history
class BrowserHistory:
def __init__(self, homepage):
self.back_stack = [homepage]
self.forward_stack = []
def visit(self, url):
self.back_stack.append(url)
self.forward_stack.clear()
def back(self, steps):
while steps > 0 and len(self.back_stack) > 1:
self.forward_stack.append(self.back_stack.pop())
steps -= 1
return self.back_stack[-1]
def forward(self, steps):
while steps > 0 and self.forward_stack:
self.back_stack.append(self.forward_stack.pop())
steps -= 1
return self.back_stack[-1]
Text Editor Undo/Redo
Two-stack pattern: Track edit history
class TextEditor:
def __init__(self):
self.undo_stack = []
self.redo_stack = []
def type(self, text):
self.undo_stack.append(('type', text))
self.redo_stack.clear()
def delete(self, count):
self.undo_stack.append(('delete', count))
self.redo_stack.clear()
def undo(self):
if self.undo_stack:
action = self.undo_stack.pop()
self.redo_stack.append(action)
def redo(self):
if self.redo_stack:
action = self.redo_stack.pop()
self.undo_stack.append(action)
DSA Expert Insight: Stack problems appear in 20% of technical interviews. Mastering LIFO principles and pattern recognition can help solve complex problems with elegant solutions. The monotonic stack pattern is particularly powerful for solving next greater/smaller element problems in O(n) time. Always consider edge cases like empty stacks and handle underflow gracefully.
You need to be logged in to participate in this discussion.