Skip to content
intermediatePhase 2 · Linear Structures

Stack

Understand LIFO data structure and its applications in expression evaluation and backtracking.

1h 15m
6 problems
Topic Progress0%

Stack Fundamentals

Stack Fundamentals

A stack is a Last In First Out (LIFO) data structure. Think of a stack of plates - you can only add or remove from the top.

Core Operations

Operation Description Time
push Add element to top O(1)
pop Remove element from top O(1)
peek/top View top element O(1)
isEmpty Check if empty O(1)

Visual Example

push(1) → [1]
push(2) → [1, 2]
push(3) → [1, 2, 3]
peek()   → returns 3
pop()    → returns 3, stack becomes [1, 2]

Java Stack Implementation

// Using Java's built-in Stack
Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
int top = stack.peek();  // 2
int val = stack.pop();   // 2
boolean empty = stack.isEmpty(); // false

// Using Deque (preferred in modern Java)
Deque<Integer> deque = new ArrayDeque<>();
deque.push(1);  // or deque.addFirst(1)
deque.push(2);
int top = deque.peek();  // 2
int val = deque.pop();   // 2

Why Use Stack?

  1. Undo operations - text editors, browsers
  2. Function calls - recursion uses call stack
  3. Expression evaluation - postfix, infix
  4. Balanced parentheses - compiler syntax checking
  5. Depth-First Search - graph traversal
  6. Backtracking - maze solving, permutations

Stack Applications

Stack Applications

1. Balanced Parentheses (LeetCode 20)

public boolean isValid(String s) {
    Deque<Character> stack = new ArrayDeque<>();
    for (char c : s.toCharArray()) {
        if (c == '(' || c == '[' || c == '{') {
            stack.push(c);
        } else {
            if (stack.isEmpty()) return false;
            char top = stack.pop();
            if ((c == ')' && top != '(') ||
                (c == ']' && top != '[') ||
                (c == '}' && top != '{')) {
                return false;
            }
        }
    }
    return stack.isEmpty();
}
// Time: O(n), Space: O(n)

2. Min Stack (LeetCode 155)

class MinStack {
    Deque<Integer> stack;
    Deque<Integer> minStack;
    
    public MinStack() {
        stack = new ArrayDeque<>();
        minStack = new ArrayDeque<>();
    }
    
    public void push(int val) {
        stack.push(val);
        if (minStack.isEmpty() || val <= minStack.peek()) {
            minStack.push(val);
        }
    }
    
    public int pop() {
        int val = stack.pop();
        if (val == minStack.peek()) {
            minStack.pop();
        }
        return val;
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int getMin() {
        return minStack.peek();
    }
}

3. Evaluate Reverse Polish Notation (LeetCode 150)

public int evalRPN(String[] tokens) {
    Deque<Integer> stack = new ArrayDeque<>();
    for (String token : tokens) {
        switch (token) {
            case "+": stack.push(stack.pop() + stack.pop()); break;
            case "-":
                int b = stack.pop(), a = stack.pop();
                stack.push(a - b);
                break;
            case "*": stack.push(stack.pop() * stack.pop()); break;
            case "/":
                b = stack.pop(); a = stack.pop();
                stack.push(a / b);
                break;
            default: stack.push(Integer.parseInt(token));
        }
    }
    return stack.pop();
}

4. Next Greater Element (LeetCode 496)

public int[] nextGreaterElement(int[] nums1, int[] nums2) {
    Map<Integer, Integer> map = new HashMap<>();
    Deque<Integer> stack = new ArrayDeque<>();
    
    for (int num : nums2) {
        while (!stack.isEmpty() && stack.peek() < num) {
            map.put(stack.pop(), num);
        }
        stack.push(num);
    }
    
    int[] result = new int[nums1.length];
    for (int i = 0; i < nums1.length; i++) {
        result[i] = map.getOrDefault(nums1[i], -1);
    }
    return result;
}

When to Use Stack

Problem Type Pattern
Balanced parentheses Push open, pop on close
Next greater/smaller Monotonic stack
Expression evaluation Shunting yard
Undo operations Push state, pop to undo
DFS traversal Push neighbors

Interactive Visualization

Stack Push/Pop Operations

Press Play or Step to begin
CurrentFound / DoneEliminatedUnvisited

Practice Problems

0/4solved
Valid Parentheses
Stack

Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.

Example:

Input: s = "()"

Output: true

Simple valid parentheses.

Optimal Solution — O(n) time, O(n) space

Stack to match opening and closing brackets

class Solution {
    public boolean isValid(String s) {
        Deque<Character> stack = new ArrayDeque<>();
        for (char c : s.toCharArray()) {
            if (c == '(' || c == '[' || c == '{') stack.push(c);
            else {
                if (stack.isEmpty()) return false;
                char top = stack.pop();
                if ((c == ')' && top != '(') || (c == ']' && top != '[') || (c == '}' && top != '{')) return false;
            }
        }
        return stack.isEmpty();
    }
}

Edge Cases:

  • Empty string
  • Single character
  • Only opening brackets
Min Stack
Stack with Auxiliary

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.

Example:

Input: push(-2), push(0), push(-3), getMin() → -3, pop(), top() → 0, getMin() → -2

Output: Operations work correctly

Min stack tracks minimum at each level.

Optimal Solution — O(1) for all operations time, O(n) space

Two stacks: main stack and min stack

class MinStack {
    Deque<Integer> stack;
    Deque<Integer> minStack;
    
    public MinStack() {
        stack = new ArrayDeque<>();
        minStack = new ArrayDeque<>();
    }
    
    public void push(int val) {
        stack.push(val);
        if (minStack.isEmpty() || val <= minStack.peek()) {
            minStack.push(val);
        }
    }
    
    public int pop() {
        int val = stack.pop();
        if (val == minStack.peek()) minStack.pop();
        return val;
    }
    
    public int top() { return stack.peek(); }
    public int getMin() { return minStack.peek(); }
}

Edge Cases:

  • Pop minimum element
  • Push duplicate minimum
  • Single element
Daily Temperatures
Monotonic Stack

Given an array of integers temperatures, return an array answer such that answer[i] is the number of days you have to wait after the ith day to get a warmer temperature.

Example:

Input: temperatures = [73,74,75,71,69,72,76,73]

Output: [1,1,4,2,1,1,0,0]

Day 0 (73): wait 1 day for 74. Day 2 (75): wait 4 days for 76.

Optimal Solution — O(n) time, O(n) space

Monotonic stack: store indices, pop when warmer found

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int n = temperatures.length;
        int[] answer = new int[n];
        Deque<Integer> stack = new ArrayDeque<>();
        
        for (int i = 0; i < n; i++) {
            while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
                int prevIndex = stack.pop();
                answer[prevIndex] = i - prevIndex;
            }
            stack.push(i);
        }
        return answer;
    }
}

Edge Cases:

  • Already sorted ascending
  • All same temperatures
  • Single element
Largest Rectangle in Histogram
Monotonic Stack

Given an array of integers heights representing the histogram's bar height, find the area of the largest rectangle in the histogram.

Example:

Input: heights = [2,1,5,6,2,3]

Output: 10

The largest rectangle is formed by bars at index 2 and 3 with height 5 and width 2.

Optimal Solution — O(n) time, O(n) space

Monotonic stack: find left and right boundaries for each bar

class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        Deque<Integer> stack = new ArrayDeque<>();
        int maxArea = 0;
        
        for (int i = 0; i <= n; i++) {
            int h = (i == n) ? 0 : heights[i];
            while (!stack.isEmpty() && h < heights[stack.peek()]) {
                int height = heights[stack.pop()];
                int width = stack.isEmpty() ? i : i - stack.peek() - 1;
                maxArea = Math.max(maxArea, height * width);
            }
            stack.push(i);
        }
        return maxArea;
    }
}

Edge Cases:

  • Single bar
  • Already sorted
  • All same heights

Quiz

1. What principle does a stack follow?

Question 1 options

2. What is the time complexity of push and pop operations?

Question 2 options

3. What is the primary purpose of Stack?

Question 3 options

4. What is a common mistake when implementing Stack?

Question 4 options

Flashcards

Question

What is LIFO?

Answer

Last In First Out - the most recently added element is removed first.

Question

When should I use a stack?

Answer

For balanced parentheses, expression evaluation, undo operations, DFS, and backtracking.

Question

What is Stack?

Answer

Stack is a key concept in software engineering.

Question

When to use Stack?

Answer

Use Stack when building production systems that require reliability, scalability, and maintainability.

Question

Stack best practices

Answer

Follow SOLID principles, write clean code, test thoroughly, document decisions, and monitor in production.

Revision Notes

Key Takeaways

  • 1.Stack is LIFO
  • 2.All operations are O(1)
  • 3.Use for balanced parentheses and expression evaluation
  • 4.Deque is preferred over Stack class

Interview Tips

  • Always mention LIFO principle
  • Discuss when to use stack vs queue
  • Explain space complexity for nested structures

Cheat Sheet

Stack Cheat Sheet

Operations: push, pop, peek, isEmpty - all O(1)
Use Cases: Balanced parentheses, expression eval, undo, DFS
Java: Use Deque interface with ArrayDeque
Pattern: Push on open bracket, pop on close bracket