Stacks

A stack is a linear data structure that follows the Last In, First Out (LIFO) principle. Elements are added and removed from the same end, called the top of the stack. Think of it like a stack of plates - you can only add or remove plates from the top.

Stacks are fundamental in computer science and are used in function calls, expression evaluation, undo operations, and many algorithms.

Stack Operations

The main operations on a stack are:

  • push(): Add an element to the top
  • pop(): Remove the top element
  • top()/peek(): View the top element without removing it
  • empty(): Check if the stack is empty
  • size(): Get the number of elements

Stack Implementation using Array

#include <iostream>
using namespace std;

class Stack {
private:
    int* arr;
    int capacity;
    int topIndex;

public:
    Stack(int size) {
        arr = new int[size];
        capacity = size;
        topIndex = -1;
    }
    
    ~Stack() {
        delete[] arr;
    }
    
    // Push element to top
    void push(int value) {
        if (topIndex >= capacity - 1) {
            cout << "Stack Overflow! Cannot push " << value << endl;
            return;
        }
        arr[++topIndex] = value;
        cout << "Pushed: " << value << endl;
    }
    
    // Pop element from top
    int pop() {
        if (topIndex < 0) {
            cout << "Stack Underflow! Cannot pop from empty stack" << endl;
            return -1;
        }
        return arr[topIndex--];
    }
    
    // Peek at top element
    int top() {
        if (topIndex < 0) {
            cout << "Stack is empty" << endl;
            return -1;
        }
        return arr[topIndex];
    }
    
    // Check if stack is empty
    bool empty() {
        return topIndex < 0;
    }
    
    // Get size of stack
    int size() {
        return topIndex + 1;
    }
    
    // Display stack contents
    void display() {
        if (empty()) {
            cout << "Stack is empty" << endl;
            return;
        }
        
        cout << "Stack contents (top to bottom): ";
        for (int i = topIndex; i >= 0; i--) {
            cout << arr[i] << " ";
        }
        cout << endl;
    }
};

int main() {
    Stack stack(5);
    
    // Push elements
    stack.push(10);
    stack.push(20);
    stack.push(30);
    
    stack.display();
    cout << "Top element: " << stack.top() << endl;
    cout << "Stack size: " << stack.size() << endl;
    
    // Pop elements
    cout << "Popped: " << stack.pop() << endl;
    cout << "Popped: " << stack.pop() << endl;
    
    stack.display();
    
    // Try to pop from empty stack
    stack.pop();
    stack.pop(); // This will cause underflow
    
    return 0;
}

STL Stack

C++ Standard Template Library provides a stack container adapter that can be used with different underlying containers:

Using STL Stack

#include <iostream>
#include <stack>
#include <vector>
#include <deque>
using namespace std;

int main() {
    // Default stack (uses deque as underlying container)
    stack<int> st;
    
    // Push elements
    st.push(10);
    st.push(20);
    st.push(30);
    st.push(40);
    
    cout << "Stack size: " << st.size() << endl;
    cout << "Top element: " << st.top() << endl;
    
    // Display and pop all elements
    cout << "Stack contents: ";
    while (!st.empty()) {
        cout << st.top() << " ";
        st.pop();
    }
    cout << endl;
    
    // Stack with vector as underlying container
    stack<int, vector<int>> stackWithVector;
    stackWithVector.push(1);
    stackWithVector.push(2);
    stackWithVector.push(3);
    
    cout << "Vector-based stack: ";
    while (!stackWithVector.empty()) {
        cout << stackWithVector.top() << " ";
        stackWithVector.pop();
    }
    cout << endl;
    
    // Stack of strings
    stack<string> stringStack;
    stringStack.push("First");
    stringStack.push("Second");
    stringStack.push("Third");
    
    cout << "String stack: ";
    while (!stringStack.empty()) {
        cout << stringStack.top() << " ";
        stringStack.pop();
    }
    cout << endl;
    
    return 0;
}

Stack Applications

Balanced Parentheses Check

One of the most common applications of stacks is checking if parentheses are balanced in an expression:

Balanced Parentheses Checker

#include <iostream>
#include <stack>
#include <string>
using namespace std;

bool isBalanced(string expression) {
    stack<char> s;
    
    for (char ch : expression) {
        // Push opening brackets
        if (ch == '(' || ch == '[' || ch == '{') {
            s.push(ch);
        }
        // Check closing brackets
        else if (ch == ')' || ch == ']' || ch == '}') {
            if (s.empty()) {
                return false; // No matching opening bracket
            }
            
            char top = s.top();
            s.pop();
            
            // Check if brackets match
            if ((ch == ')' && top != '(') ||
                (ch == ']' && top != '[') ||
                (ch == '}' && top != '{')) {
                return false;
            }
        }
    }
    
    // Stack should be empty for balanced expression
    return s.empty();
}

int main() {
    vector<string> expressions = {
        "()",
        "()[]{}", 
        "([{}])",
        "(((",
        "())",
        "([)]",
        "{[()()]}"
    };
    
    cout << "Checking balanced parentheses:" << endl;
    for (const string& expr : expressions) {
        cout << expr << " -> " 
             << (isBalanced(expr) ? "Balanced" : "Not Balanced") 
             << endl;
    }
    
    return 0;
}

Expression Evaluation

Stacks can be used to evaluate postfix expressions (Reverse Polish Notation):

Postfix Expression Evaluation

#include <iostream>
#include <stack>
#include <string>
#include <sstream>
using namespace std;

int evaluatePostfix(string expression) {
    stack<int> s;
    istringstream iss(expression);
    string token;
    
    while (iss >> token) {
        if (token == "+" || token == "-" || token == "*" || token == "/") {
            // Pop two operands
            int operand2 = s.top(); s.pop();
            int operand1 = s.top(); s.pop();
            
            int result;
            if (token == "+") result = operand1 + operand2;
            else if (token == "-") result = operand1 - operand2;
            else if (token == "*") result = operand1 * operand2;
            else if (token == "/") result = operand1 / operand2;
            
            s.push(result);
        } else {
            // Convert token to number and push
            s.push(stoi(token));
        }
    }
    
    return s.top();
}

int main() {
    vector<string> expressions = {
        "3 4 +",           // 3 + 4 = 7
        "5 2 * 3 +",       // 5 * 2 + 3 = 13
        "15 7 1 1 + - / 3 * 2 1 1 + + -", // Complex expression
        "2 3 1 * + 9 -"    // 2 + 3 * 1 - 9 = -4
    };
    
    cout << "Postfix Expression Evaluation:" << endl;
    for (const string& expr : expressions) {
        cout << expr << " = " << evaluatePostfix(expr) << endl;
    }
    
    return 0;
}