Stack Data Structure Reference

Introduction to Stacks

A stack is a linear data structure that follows the Last In, First Out (LIFO) principle. This means that the last element added to the stack will be the first one to be removed. Stacks are used in many applications, including expression evaluation, backtracking algorithms, and managing function calls in recursion.

Key Operations: Push, Pop, Peek, IsEmpty, IsFull

Time Complexity: All operations are O(1)

Space Complexity: O(n)

Implementing a Stack

A stack can be implemented using an array, a linked list, or the built-in stack classes in some programming languages. Below is a simple implementation of a stack using an array in C++:

Example (C++):

#include 
#define MAX 1000

class Stack {
    int top;

public:
    int arr[MAX]; // Maximum size of Stack

    Stack() { top = -1; }

    bool push(int x);
    int pop();
    int peek();
    bool isEmpty();
    bool isFull();
};

// Function to add an item to the stack. Increases top by 1
bool Stack::push(int x) {
    if (top >= (MAX - 1)) {
        std::cout << "Stack Overflow\n";
        return false;
    } else {
        arr[++top] = x;
        std::cout << x << " pushed into stack\n";
        return true;
    }
}

// Function to remove an item from the stack. Decreases top by 1
int Stack::pop() {
    if (top < 0) {
        std::cout << "Stack Underflow\n";
        return 0;
    } else {
        int x = arr[top--];
        return x;
    }
}

// Function to return the top element without removing it
int Stack::peek() {
    if (top < 0) {
        std::cout << "Stack is Empty\n";
        return 0;
    } else {
        int x = arr[top];
        return x;
    }
}

// Function to check if the stack is empty
bool Stack::isEmpty() {
    return (top < 0);
}

// Function to check if the stack is full
bool Stack::isFull() {
    return (top >= (MAX - 1));
}

int main() {
    Stack stack;
    stack.push(10);
    stack.push(20);
    stack.push(30);

    std::cout << stack.pop() << " popped from stack\n";

    return 0;
}
            

This code demonstrates how to implement a stack using an array with all basic stack operations: push, pop, peek, isEmpty, and isFull.

Stack Operations

1. Push Operation

The push operation adds an element to the top of the stack. Before adding the element, we check if the stack is full to avoid stack overflow.

Example (C++):

// Function to add an item to the stack. Increases top by 1
bool Stack::push(int x) {
    if (top >= (MAX - 1)) {
        std::cout << "Stack Overflow\n";
        return false;
    } else {
        arr[++top] = x;
        std::cout << x << " pushed into stack\n";
        return true;
    }
}
            

2. Pop Operation

The pop operation removes the element from the top of the stack and returns it. Before removing, we check if the stack is empty to avoid stack underflow.

Example (C++):

// Function to remove an item from the stack. Decreases top by 1
int Stack::pop() {
    if (top < 0) {
        std::cout << "Stack Underflow\n";
        return 0;
    } else {
        int x = arr[top--];
        return x;
    }
}
            

3. Peek Operation

The peek operation returns the top element of the stack without removing it. This is useful when you need to check the top element but don't want to modify the stack.

Example (C++):

// Function to return the top element without removing it
int Stack::peek() {
    if (top < 0) {
        std::cout << "Stack is Empty\n";
        return 0;
    } else {
        int x = arr[top];
        return x;
    }
}
            

4. IsEmpty Operation

The isEmpty operation checks if the stack is empty by verifying if the top index is less than 0. This operation is useful for checking if any more elements are left to process.

Example (C++):

// Function to check if the stack is empty
bool Stack::isEmpty() {
    return (top < 0);
}
            

5. IsFull Operation

The isFull operation checks if the stack has reached its maximum capacity by verifying if the top index is equal to or greater than the maximum allowed index.

Example (C++):

// Function to check if the stack is full
bool Stack::isFull() {
    return (top >= (MAX - 1));
}
            

Time and Space Complexity

Stacks are very efficient in terms of time complexity for their operations:

The space complexity of a stack is O(n), where n is the number of elements stored in the stack. The space required depends on the maximum number of elements the stack can hold.

When to Use Stacks

Stacks are useful in many scenarios, particularly when you need to manage data in a LIFO manner. Here are some common use cases:

1. Function Call Management

Stacks are used to manage function calls in programming languages. When a function is called, its context (variables, return address) is pushed onto the stack. When the function returns, the context is popped off the stack.

2. Expression Evaluation

Stacks are used to evaluate expressions, particularly in parsing operations such as converting infix expressions to postfix (Reverse Polish Notation) or prefix notation, and in evaluating those expressions.

3. Undo Mechanisms

Stacks are often used to implement undo mechanisms in text editors, drawing applications, and other software. Each change is pushed onto the stack, and when the user chooses to undo, the last change is popped off the stack.

4. Backtracking Algorithms

In algorithms that require exploring all possibilities (e.g., maze solving, puzzle solving), stacks are used to keep track of the current path. If a dead end is reached, the algorithm can backtrack by popping the stack.

5. Syntax Parsing

Compilers use stacks to parse syntax in programming languages, checking for matching parentheses, braces, and other delimiters.

6. Browser History

Web browsers use stacks to keep track of the user's navigation history. Each page visited is pushed onto the stack, and the back button pops the top page off to return to the previous page.