Skip to content

Stack Visualizer

1/1

What is a stack?

A stack is a linear data structure that follows LIFO — last in, first out. You can only add (push), remove (pop) or read (peek) the element on top, and each of those operations takes O(1) time. Picture a pile of plates: the plate you put down last is the first one you pick up.

How it works

  1. Store the elements in an array A with a fixed capacity and keep top, the index of the newest element. top = -1 means the stack is empty.
  2. push(x): if top == capacity - 1 the stack is full (overflow). Otherwise increment top and write A[top] = x.
  3. pop(): if top == -1 the stack is empty (underflow). Otherwise read x = A[top], decrement top and return x.
  4. peek(): return A[top] without changing top.
  5. isEmpty() is top == -1, isFull() is top == capacity - 1, and the size is top + 1.

Worked example: push 5, push 9, pop, push 2

Start with an empty stack of capacity 5 (top = -1):

  • push(5): top becomes 0 and A[0] = 5. Stack: [5].
  • push(9): top becomes 1 and A[1] = 9. Stack: [5, 9].
  • pop(): reads A[1] = 9, top drops to 0, and 9 is returned. Stack: [5].
  • push(2): top becomes 1 again and A[1] = 2 overwrites the old 9. Stack: [5, 2].

Notice that pop never erased the 9 — it only moved top. The old value stayed in the array as garbage until the next push overwrote it. That is why pop, and even clear() (just top = -1), are O(1): no element other than the top is touched. In a garbage-collected language that stores object references, set the freed slot to None or null so the object can be reclaimed — still O(1) per pop, though a clear() that releases every reference must null each slot and becomes O(n).

Overflow, underflow and growable stacks

A fixed-size array stack can fail in two ways. Overflow is pushing onto a full stack — there is no A[capacity]. Underflow is popping or peeking an empty stack — there is no A[-1]. Check isFull() and isEmpty() first, or raise an error as the code below does.

Most real stacks avoid overflow by growing: when the array fills up, allocate a larger one (typically twice the size) and copy the elements across. A single resize costs O(n), but growing by a constant factor makes resizes so rare that push is amortized O(1). Python's list, Java's ArrayDeque and C++'s std::vector all grow this way.

The famous *stack overflow* error is the same idea applied to the call stack: every function call pushes a frame, and infinite or very deep recursion runs out of the memory reserved for it.

Array stack vs linked-list stack

  • Array: elements sit next to each other in memory, so it is cache-friendly and has no per-element pointer overhead. A growable array pays for an occasional O(n) resize.
  • Linked list: push and pop at the head are O(1) in the worst case and it never needs resizing, but every node carries a pointer and lives somewhere else in memory.

Both give exactly the same LIFO behaviour. In practice array-backed stacks are usually faster; a linked list is the natural choice when you are already working with nodes.

Where stacks are used

  • Function calls: the call stack stores return addresses and local variables, which is why recursion unwinds in reverse order.
  • Undo and redo in editors, and the back button in a browser.
  • Parsing: matching brackets, converting infix expressions to postfix and evaluating them.
  • Depth-first search and backtracking — mazes, Sudoku, permutations — where an explicit stack replaces recursion.
  • Monotonic stacks for next greater element, stock span and the largest rectangle in a histogram.

Common mistakes

  • Popping or peeking without checking that the stack is non-empty.
  • Mixing up pop (removes the top) and peek (only reads it).
  • Off-by-one errors between the top index and the size: the size is top + 1.
  • Using list.pop(0) or list.insert(0, x) in Python — that works on the bottom of the list and costs O(n). A stack uses the end.
  • In Java, reaching for the legacy Stack class (synchronized, extends Vector) instead of ArrayDeque.

Interview questions on stacks

  • Valid parentheses: push openers, match each closer against the top.
  • Min stack: keep a second stack of running minimums so getMin() is O(1).
  • Queue using two stacks: amortized O(1) per operation.
  • Evaluate reverse Polish notation, daily temperatures, decode string and largest rectangle in histogram are all one-stack solutions.

The visualizer above lets you rehearse the basics: fill the stack to overflow, pop it to underflow, and use *Quiz me* to predict what the next pop returns.

Time and space complexity

CaseTimeSpaceWhy
pushO(1)O(1)Writes one slot at top + 1; amortized O(1) if the array has to grow.
popO(1)O(1)Reads A[top] and moves top down; nothing else moves.
peek / isEmpty / isFullO(1)O(1)A single read or comparison.
search for a valueO(n)O(1)There is no access to the middle; you scan from the top.
n elements stored—O(n)One array slot per element (plus unused capacity).

Stack in Python

class Stack:
    """Array-backed stack with a fixed capacity (LIFO)."""

    def __init__(self, capacity):
        self.A = [None] * capacity
        self.capacity = capacity
        self.top = -1  # index of the top; -1 = empty

    def push(self, x):
        if self.top == self.capacity - 1:
            raise IndexError("Stack Overflow")
        self.top = self.top + 1
        self.A[self.top] = x

    def pop(self):
        if self.top == -1:
            raise IndexError("Stack Underflow")
        x = self.A[self.top]
        self.top = self.top - 1  # A[top + 1] stays behind as garbage
        return x

    def peek(self):
        if self.top == -1:
            raise IndexError("Stack is empty")
        return self.A[self.top]  # top unchanged

    def is_empty(self):
        return self.top == -1

    def is_full(self):
        return self.top == self.capacity - 1

    def clear(self):
        self.top = -1  # O(1): the old values are now garbage


# s = Stack(5); s.push(5); s.push(9); s.pop()  -> 9

The visualizer above shows the same code in Python, C++, Java and JavaScript.

Frequently asked questions

Is a stack LIFO or FIFO?

A stack is LIFO (last in, first out): the most recently pushed element is the first one popped. A queue is the FIFO counterpart.

What is the time complexity of push and pop?

Both are O(1) because they only touch the top element. With a dynamic array that doubles when it is full, push is amortized O(1).

What is the difference between pop and peek?

pop() removes the top element and returns it; peek() (also called top()) returns it without removing it. Both fail on an empty stack.

What are stack overflow and stack underflow?

Overflow is pushing onto a full fixed-size stack; underflow is popping or peeking an empty one. Check isFull() or isEmpty() first, or use a stack that grows.

How do you implement a stack in Python?

Use a list: append(x) pushes, pop() pops and s[-1] peeks, all O(1) amortized. The class above shows the same thing with an explicit array and top index.

Can a stack be implemented with a linked list?

Yes. Push and pop at the head of a singly linked list are both O(1) and the stack never fills up; the cost is one pointer per element.

Keep learning

Last updated