Lesson 34 of 50 intermediate

Stack — The Pancake Pile

Last In, First Out — the data structure that undo buttons love!

Open interactive version (quiz + challenge)

Real-world analogy

A stack is like a stack of pancakes. When you make a new pancake, you put it on TOP of the pile. When you eat one, you take the TOP one first. You can't eat the one at the bottom without removing all the ones above it. Last pancake in, first pancake eaten — that's LIFO: Last In, First Out!

What is it?

A stack is a fundamental data structure that follows the LIFO (Last In, First Out) principle. You can only add (push) and remove (pop) elements from the top. It's like a stack of pancakes or plates — you always deal with the top item. Stacks are essential for bracket matching, undo operations, expression evaluation, and many competitive programming problems.

Real-world relevance

Your browser's back button is a stack of visited pages. The Ctrl+Z undo feature in every text editor uses a stack of actions. Call centers put callers on hold in a stack (sometimes!). Even the way your computer runs functions (the call stack) is a stack. Compilers use stacks to check if your parentheses and brackets are balanced.

Key points

Code example

#include <bits/stdc++.h>
using namespace std;

// Check if brackets are balanced
bool isBalanced(string s) {
    stack<char> st;
    for (char c : s) {
        if (c == '(' || c == '{' || c == '[') {
            st.push(c);  // Push opening brackets
        } else if (c == ')' || c == '}' || c == ']') {
            if (st.empty()) return false;  // No matching opener
            char top = st.top();
            st.pop();
            // Check if the brackets match
            if (c == ')' && top != '(') return false;
            if (c == '}' && top != '{') return false;
            if (c == ']' && top != '[') return false;
        }
    }
    return st.empty();  // All brackets matched?
}

int main() {
    // Basic stack operations
    stack<int> s;
    s.push(10);   // Stack: [10]
    s.push(20);   // Stack: [10, 20]
    s.push(30);   // Stack: [10, 20, 30]
    
    cout << "Top: " << s.top() << "\n";    // 30
    cout << "Size: " << s.size() << "\n";   // 3
    
    s.pop();  // Remove 30. Stack: [10, 20]
    cout << "Top after pop: " << s.top() << "\n";  // 20
    
    // Bracket matching
    cout << "({[]}) balanced? " << (isBalanced("({[]})") ? "YES" : "NO") << "\n";
    cout << "({[}) balanced? " << (isBalanced("({[})") ? "YES" : "NO") << "\n";
    
    return 0;
}

Line-by-line walkthrough

  1. 1. #include — includes everything, including the stack library.
  2. 2. bool isBalanced(string s) — a function that takes a string and returns true if all brackets are properly matched and nested.
  3. 3. stack st; — we create a stack that holds characters. We'll push opening brackets onto it.
  4. 4. for (char c : s) — we loop through every character in the string, one at a time.
  5. 5. if (c == '(' || c == '{' || c == '[') st.push(c); — when we see an opening bracket, we push it onto the stack. We'll check for its match later.
  6. 6. if (st.empty()) return false; — if we see a closing bracket but the stack is empty, there's no matching opening bracket, so the string is NOT balanced.
  7. 7. char top = st.top(); st.pop(); — we get the top of the stack (the most recent opening bracket) and remove it. Then we check if it matches the closing bracket we found.
  8. 8. return st.empty(); — at the end, if the stack is empty, every opening bracket found its match. If not, there are unmatched opening brackets.
  9. 9. In main(), we demonstrate push, top, size, and pop operations, then test our bracket matching function with two examples.

Spot the bug

#include <bits/stdc++.h>
using namespace std;

int main() {
    stack<int> s;
    s.push(10);
    s.push(20);
    s.push(30);
    
    while (s.size() > 0) {
        s.pop();
        cout << s.top() << " ";
    }
    return 0;
}
Need a hint?
Think about the ORDER of pop() and top(). What are you printing? And what happens on the very last iteration?
Show answer
TWO bugs! First, pop() is called BEFORE top(), so we never print the value 30 — it gets removed before we can see it. The order should be: top() first, THEN pop(). Second, after popping the last element, the stack is empty, but we try to call top() on an empty stack, causing a crash. Fix: swap the order to 'cout << s.top(); s.pop();' — this prints the value before removing it, and since we print then pop, we never access an empty stack's top.

Explain like I'm 5

Imagine you have a tube that's closed at the bottom and open at the top. You can drop balls in from the top and take balls out from the top. If you drop in a red ball, then a blue ball, then a green ball — the green ball is on top! To get the red ball, you'd have to take out the green one first, then the blue one, then finally the red one. That's a stack — last ball in is the first ball out!

Fun fact

The stack data structure was first proposed in 1946 by Alan Turing, the father of computer science! He called it a 'burial' and 'unburial' system. The bracket matching algorithm is used billions of times per day by compilers and text editors around the world to check if your code has matching parentheses!

Hands-on challenge

Write a program that uses a stack to reverse a string. Read a string from input, push each character onto a stack, then pop all characters and print them. For example, input 'hello' should output 'olleh'. Bonus: modify your bracket matching to also handle angle brackets !

More resources

Open interactive version (quiz + challenge) ← Back to course: CP Zero to Hero