Stacks

Last in, first out. Function calls, undo, bracket matching and postfix evaluation.

A stack is a collection where the last item added is the first one removed: LIFO (last in, first out). Think of a pile of plates: you put plates on top and take them from the top.

OperationMeaningTime
push(x)put x on topO(1)
pop()remove and return the top itemO(1)
peek()look at the top item without removing itO(1)
isEmpty()is the stack empty?O(1)
push(1)[1]push(2)[1, 2]push(3)[1, 2, 3]pop() → 3[1, 2]
push(1), push(2), push(3), then pop() returns 3

Stacks are everywhere: the call stack keeps track of function calls (that is why deep recursion causes a "stack overflow"), editors use one for undo, and compilers use them to check brackets and evaluate expressions.

An array-based stack

public class Main {
    public static void main(String[] args) {
        Stack s = new Stack(4);
        s.push(1);
        s.push(2);
        s.push(3);
        System.out.println("peek: " + s.peek());
        System.out.println("pop: " + s.pop());
        System.out.println("pop: " + s.pop());
        System.out.println("size: " + s.size());
        s.pop();
        try {
            s.pop();
        } catch (IllegalStateException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

class Stack {
    private final int[] data;
    private int top = -1;              // index of the top item, -1 when empty

    Stack(int capacity) { data = new int[capacity]; }

    void push(int x) {
        if (top == data.length - 1) throw new IllegalStateException("stack overflow");
        data[++top] = x;
    }

    int pop() {
        if (top == -1) throw new IllegalStateException("stack underflow");
        return data[top--];
    }

    int peek() { return data[top]; }
    int size() { return top + 1; }
}
Output
peek: 3
pop: 3
pop: 2
size: 1
Error: stack underflow

Checking balanced brackets

Push every opening bracket. For every closing bracket, the top of the stack must be the matching opener. At the end the stack must be empty.

Read next character cc is an opening bracket?YesNopush(c)c is a closing bracket?YesNopop and compare with cNext character (at the end, the stack must be empty)
Checking one character of the expression
import java.util.ArrayDeque;
import java.util.Deque;

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

    public static void main(String[] args) {
        for (String s : new String[]{"{a[b(c)]}", "(a+b))", "([)]", "((x)"}) {
            System.out.println(s + " -> " + balanced(s));
        }
    }
}
Output
{a[b(c)]} -> true
(a+b)) -> false
([)] -> false
((x) -> false

Evaluating postfix expressions

In postfix (Reverse Polish) notation the operator comes after its operands: 3 4 + means 3 + 4, and 5 1 2 + 4 * + 3 - means 5 + (1 + 2) × 4 − 3. Postfix needs no brackets, and a stack evaluates it in one pass: push numbers; on an operator, pop two values, apply it, and push the result.

import java.util.ArrayDeque;
import java.util.Deque;

public class Main {
    static int evalPostfix(String expr) {
        Deque<Integer> stack = new ArrayDeque<>();
        for (String tok : expr.split(" ")) {
            switch (tok) {
                case "+", "-", "*", "/" -> {
                    int b = stack.pop(), a = stack.pop();     // note the order
                    stack.push(switch (tok) {
                        case "+" -> a + b;
                        case "-" -> a - b;
                        case "*" -> a * b;
                        default -> a / b;
                    });
                }
                default -> stack.push(Integer.parseInt(tok));
            }
        }
        return stack.pop();
    }

    public static void main(String[] args) {
        System.out.println(evalPostfix("3 4 +"));
        System.out.println(evalPostfix("5 1 2 + 4 * + 3 -"));
        System.out.println(evalPostfix("8 2 3 * - 4 2 / +"));
    }
}
Output
7
14
4

GATE-style practice questions

GATE-styleMCQ · one correct answer

The numbers 1, 2, 3, 4 are pushed onto an empty stack in this order, and pops may happen at any time in between. Which output sequence is impossible?

Show answer and explanationAnswer: C

To output 3 first, 1 and 2 must already be on the stack with 2 on top. The next pop must then give 2, not 1, so 3 1 2 4 cannot happen. The others are possible, for example 2 1 4 3 is push 1, push 2, pop, pop, push 3, push 4, pop, pop.

GATE-styleNAT · numerical answer

What is the value of the postfix expression 9 3 / 4 2 * + 5 - ? (All operators are binary; / is integer division.)

Show answer and explanationAnswer: 6

9 3 / gives 3; 4 2 * gives 8; 3 8 + gives 11; 11 5 - gives 6.

GATE-styleNAT · numerical answer

A stack is used to check the bracket string ( ( [ ] ) ( ) ). What is the maximum number of items on the stack at any time?

Show answer and explanationAnswer: 3

After reading (, (, [ the stack holds 3 items. ] and ) then pop two, ( pushes one (2 items), and the rest only pop. So the maximum is 3.

GATE-styleMSQ · one or more correct answers

Which of the following are typical applications of a stack? (Select all that apply.)

Show answer and explanationAnswer: A, B, D

Function calls, undo and infix-to-postfix conversion all need the most recent item first (LIFO). Print jobs in arrival order need a queue (FIFO).