Stacks

November 19, 2025
#stack #data-structures #lifo #algorithms #interviewing

Introduction

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 take off.

Stacks are fundamental in computer science and appear everywhere—from the call stack that tracks function calls in your program to undo/redo functionality in text editors. By the end of this tutorial, you’ll understand how stacks work, when to use them, and how to implement them efficiently.

If you’re new to time and space complexity, start with our guide to Big-O Notation. You may also want to review Arrays and Linked Lists to understand the underlying implementation options.

What is a stack?

A stack is a collection of elements with two principal operations:

  1. Push: Add an element to the top of the stack
  2. Pop: Remove and return the element from the top of the stack

Additionally, most stack implementations include: 3. Peek (or Top): View the top element without removing it 4. IsEmpty: Check if the stack is empty 5. Size: Get the number of elements in the stack

The key constraint: you can only access the most recently added element (the “top”).

Why use a stack?

Choose a stack when:

  1. You need LIFO (Last-In-First-Out) access to data
  2. You’re tracking state that needs to be reversed or unwound (e.g., undo operations, backtracking)
  3. You need to evaluate expressions or parse syntax (e.g., matching parentheses, expression evaluation)
  4. You’re implementing recursive algorithms iteratively
  5. You need to traverse trees or graphs using depth-first search (DFS)

Avoid a stack when:

  • You need random access to elements (use an array instead)
  • You need First-In-First-Out (FIFO) access (use a queue instead)
  • You need to search through all elements frequently

Real-world applications

Stacks are used in many practical scenarios:

  • Function call stack: Programming languages use a stack to track function calls and local variables
  • Browser history: The back button uses a stack to navigate previous pages
  • Undo/Redo: Text editors and graphics programs use stacks to track changes
  • Expression evaluation: Calculators use stacks to evaluate mathematical expressions
  • Syntax parsing: Compilers use stacks to parse code and match brackets/parentheses
  • Backtracking algorithms: Maze solving, puzzle solving, and game AI use stacks

Core operations and complexity

All primary stack operations are extremely efficient:

  • Push: O(1) — add element to top
  • Pop: O(1) — remove and return top element
  • Peek/Top: O(1) — view top element without removing
  • IsEmpty: O(1) — check if stack is empty
  • Size: O(1) — get number of elements
  • Space: O(n) — where n is the number of elements in the stack

Stack implementation

Stacks can be implemented using either arrays or linked lists. Array-based implementations are simpler and have better cache locality, while linked list implementations never need resizing.

Array-based stack

Linked list-based stack

For an alternative implementation using linked lists, we can use a singly linked list where we push and pop from the head:

Common stack problems

1. Balanced parentheses

Check if a string of parentheses, brackets, and braces is balanced:

2. Reverse a string

Stacks naturally reverse the order of elements:

3. Evaluate postfix expression

Evaluate expressions in Reverse Polish Notation (e.g., “2 3 + 4 *” = 20):

Stack variations and extensions

  • Min/Max Stack: Augmented stack that tracks minimum or maximum element in O(1) time
  • Two-stack queue: Implement a queue using two stacks
  • Fixed-size stack: Stack with a maximum capacity (useful for memory-constrained systems)
  • Thread-safe stack: Synchronized stack for concurrent programming

Common pitfalls

  • Stack overflow: Pushing too many elements can exhaust memory (especially with fixed-size implementations)
  • Underflow: Popping from an empty stack should be handled gracefully
  • Memory leaks: In languages with manual memory management, ensure popped nodes are freed
  • Not checking isEmpty: Always verify the stack isn’t empty before calling pop() or peek()

When to prefer other data structures

  • Need FIFO access → Use a queue
  • Need random access by index → Use an array
  • Need to search all elements → Use a hash table or sorted array with binary search
  • Need sorted data → Use a binary search tree or heap

Practice problems

  1. Implement a min-stack that supports push, pop, peek, and getMin (return minimum element) all in O(1) time
  2. Implement a queue using two stacks
  3. Use a stack to implement depth-first search (DFS) on a graph or tree
  4. Write a function to simplify a Unix file path (e.g., “/a/./b/../../c/” → “/c”)
  5. Design a browser back/forward button system using stacks
  6. Evaluate infix expressions (e.g., “2 + 3 * 4”) by converting to postfix first
  7. Implement an undo/redo system for a text editor using two stacks

Complexity summary

All core stack operations are extremely efficient:

  • Time complexity:
    • Push: O(1)
    • Pop: O(1)
    • Peek: O(1)
    • IsEmpty: O(1)
    • Size: O(1)
  • Space complexity: O(n) where n is the number of elements

This constant-time performance for all operations makes stacks one of the most efficient data structures for LIFO access patterns.

Thanks for visiting
We are actively updating content to this site. Thanks for visiting! Please bookmark this page and visit again soon.
Sponsor