Beginner
15 min
Full Guide

Stacks

Stack data structure, LIFO principle, and practical applications

What is a Stack?

A stack is a linear data structure that follows the Last In, First Out (LIFO) principle. Think of it like a stack of plates - you can only add or remove plates from the top. The last plate you place on the stack is the first one you'll remove.

Stacks are one of the most fundamental data structures in computer science and are used extensively in algorithms, memory management, and expression evaluation.

LIFO Principle

Top → [3] ← Last In, First Out
[2]
[1] ← Bottom

Operations happen at the top only!

Stack Operations

push(element) - O(1)

Add an element to the top of the stack.

pop() - O(1)

Remove and return the top element from the stack.

peek() / top() - O(1)

Return the top element without removing it.

isEmpty() - O(1)

Check if the stack is empty.

size() - O(1)

Return the number of elements in the stack.

Stack Implementation Using Array

class Stack {
  constructor() {
    this.items = [];
  }

  // Push element onto stack - O(1)
  push(element) {
    this.items.push(element);
  }

  // Pop element from stack - O(1)
  pop() {
    if (this.isEmpty()) {
      throw new Error('Stack is empty');
    }
    return this.items.pop();
  }

  // Peek at top element - O(1)
  peek() {
    if (this.isEmpty()) {
      throw new Error('Stack is empty');
    }
    return this.items[this.items.length - 1];
  }

  // Check if stack is empty - O(1)
  isEmpty() {
    return this.items.length === 0;
  }

  // Get stack size - O(1)
  size() {
    return this.items.length;
  }

  // Clear the stack - O(1)
  clear() {
    this.items = [];
  }

  // Print stack contents
  print() {
    console.log(this.items.join(' ← '));
  }
}

// Usage
const stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
stack.print();  // 1 ← 2 ← 3

console.log(stack.peek());  // 3
console.log(stack.pop());   // 3
stack.print();  // 1 ← 2

Stack Implementation Using Linked List

class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

class LinkedListStack {
  constructor() {
    this.top = null;
    this.length = 0;
  }

  // Push element - O(1)
  push(data) {
    const newNode = new Node(data);
    newNode.next = this.top;
    this.top = newNode;
    this.length++;
  }

  // Pop element - O(1)
  pop() {
    if (this.isEmpty()) {
      throw new Error('Stack is empty');
    }
    const poppedData = this.top.data;
    this.top = this.top.next;
    this.length--;
    return poppedData;
  }

  // Peek at top - O(1)
  peek() {
    if (this.isEmpty()) {
      throw new Error('Stack is empty');
    }
    return this.top.data;
  }

  isEmpty() {
    return this.length === 0;
  }

  size() {
    return this.length;
  }
}

Common Stack Problems

1. Valid Parentheses

Check if string has valid bracket matching.

function isValidParentheses(s) {
  const stack = [];
  const pairs = {
    '(': ')',
    '[': ']',
    '{': '}'
  };
  
  for (let char of s) {
    // If opening bracket, push to stack
    if (pairs[char]) {
      stack.push(char);
    } 
    // If closing bracket
    else {
      // Stack must not be empty and top must match
      const top = stack.pop();
      if (!top || pairs[top] !== char) {
        return false;
      }
    }
  }
  
  // Stack should be empty for valid string
  return stack.length === 0;
}

console.log(isValidParentheses('()[]{}'));    // true
console.log(isValidParentheses('([)]'));      // false
console.log(isValidParentheses('{[()]}'));    // true

2. Reverse a String

function reverseString(str) {
  const stack = [];
  
  // Push all characters onto stack
  for (let char of str) {
    stack.push(char);
  }
  
  // Pop all characters to build reversed string
  let reversed = '';
  while (stack.length > 0) {
    reversed += stack.pop();
  }
  
  return reversed;
}

console.log(reverseString('hello'));  // 'olleh'

3. Evaluate Postfix Expression

function evaluatePostfix(expression) {
  const stack = [];
  const tokens = expression.split(' ');
  
  for (let token of tokens) {
    // If number, push to stack
    if (!isNaN(token)) {
      stack.push(Number(token));
    } 
    // If operator, pop two operands and apply
    else {
      const b = stack.pop();
      const a = stack.pop();
      
      switch (token) {
        case '+': stack.push(a + b); break;
        case '-': stack.push(a - b); break;
        case '*': stack.push(a * b); break;
        case '/': stack.push(a / b); break;
      }
    }
  }
  
  return stack.pop();
}

console.log(evaluatePostfix('2 3 1 * + 9 -'));  // -4
// Explanation: ((2 + (3 * 1)) - 9) = -4

4. Next Greater Element

function nextGreaterElements(arr) {
  const result = new Array(arr.length).fill(-1);
  const stack = [];
  
  for (let i = 0; i < arr.length; i++) {
    // Pop elements smaller than current
    while (stack.length > 0 && arr[stack[stack.length - 1]] < arr[i]) {
      const index = stack.pop();
      result[index] = arr[i];
    }
    stack.push(i);
  }
  
  return result;
}

console.log(nextGreaterElements([4, 5, 2, 10]));  // [5, 10, 10, -1]
console.log(nextGreaterElements([2, 1, 3, 4]));   // [3, 3, 4, -1]

5. Min Stack (Get Minimum in O(1))

class MinStack {
  constructor() {
    this.stack = [];
    this.minStack = [];  // Track minimums
  }

  push(val) {
    this.stack.push(val);
    
    // Update min stack
    if (this.minStack.length === 0 || val <= this.getMin()) {
      this.minStack.push(val);
    }
  }

  pop() {
    const val = this.stack.pop();
    
    // If popped value is min, remove from min stack
    if (val === this.getMin()) {
      this.minStack.pop();
    }
    
    return val;
  }

  top() {
    return this.stack[this.stack.length - 1];
  }

  getMin() {
    return this.minStack[this.minStack.length - 1];
  }
}

const minStack = new MinStack();
minStack.push(3);
minStack.push(5);
console.log(minStack.getMin());  // 3
minStack.push(2);
console.log(minStack.getMin());  // 2
minStack.pop();
console.log(minStack.getMin());  // 3

6. Convert Infix to Postfix

function infixToPostfix(expression) {
  const precedence = { '+': 1, '-': 1, '*': 2, '/': 2, '^': 3 };
  const stack = [];
  let result = '';
  
  for (let char of expression) {
    // If operand, add to result
    if (/[a-zA-Z0-9]/.test(char)) {
      result += char;
    }
    // If '(', push to stack
    else if (char === '(') {
      stack.push(char);
    }
    // If ')', pop until '('
    else if (char === ')') {
      while (stack.length > 0 && stack[stack.length - 1] !== '(') {
        result += stack.pop();
      }
      stack.pop();  // Remove '('
    }
    // If operator
    else {
      while (
        stack.length > 0 &&
        stack[stack.length - 1] !== '(' &&
        precedence[stack[stack.length - 1]] >= precedence[char]
      ) {
        result += stack.pop();
      }
      stack.push(char);
    }
  }
  
  // Pop remaining operators
  while (stack.length > 0) {
    result += stack.pop();
  }
  
  return result;
}

console.log(infixToPostfix('a+b*c'));      // abc*+
console.log(infixToPostfix('(a+b)*c'));    // ab+c*

Real-World Applications of Stacks

🌐 Browser History

Back button uses a stack to track visited pages. Each new page is pushed, and clicking back pops the previous page.

↩️ Undo/Redo

Text editors use stacks to implement undo/redo. Each action is pushed onto the undo stack, and undo pops from it.

🔄 Function Calls

The call stack tracks function execution. When a function is called, it's pushed; when it returns, it's popped.

➕ Expression Evaluation

Compilers use stacks to parse and evaluate mathematical expressions and handle operator precedence.

🧮 Recursion

Recursive functions use the call stack implicitly. Each recursive call is pushed until the base case is reached.

🔍 DFS Algorithm

Depth-First Search uses a stack (explicit or implicit via recursion) to explore graphs and trees.

💡 Key Takeaways

  • ✓ Stack follows LIFO (Last In, First Out) principle
  • ✓ All operations are O(1) - very efficient!
  • ✓ Perfect for problems requiring backtracking or reversing
  • ✓ Used extensively in recursion, parsing, and algorithm design
  • ✓ Can be implemented with arrays or linked lists
  • ✓ Think "stack" when you need to process items in reverse order