Linear

Stack Fundamentals

LIFO order, O(1) push and pop. The structure behind function calls, undo systems, and every backtracking algorithm.

Foundations Β· Array Β· Linked List Β· Stack Β· Queue Β· HashMap Β· Binary Tree Β· BST Β· Heap Β· Trie Β· Graph Β· Sorting Β· Binary Search Β· Two Pointers Β· Sliding Window Β· Dynamic Prog.
01
Section One Β· Foundation

What is Stack?

push 40 top 30 20 10

LIFO β€” last in, first out β€” is the defining constraint of a stack: every insertion and every removal happens at one end only, the top. That constraint is what makes stacks the natural match for function call management, undo history, bracket matching, and any algorithm that must retrace its steps in reverse order. There is no random access, no searching, no way to inspect anything below the top β€” the restriction is the feature, not a limitation. Under the hood, Java's ArrayDeque implements a stack with a resizable array and a top-of-stack pointer; push and pop move that pointer in O(1) without shifting any elements. The JVM's own call stack is literally a stack of frames β€” each method call pushes a frame, each return pops one, and StackOverflowError means the stack ran out of space.

Analogy: A stack of dinner plates in a cafeteria spring-loaded dispenser. You can only take the top plate β€” you cannot reach in and grab the third one without removing the two above it first. Every plate you add goes on top, and every plate you remove comes from the top. The last plate added is always the first one taken. That constraint β€” LIFO β€” is not a limitation; it is the entire point.
02
Section Two Β· Mental Model

How It Thinks

Only the top element is accessible
β–Ά
Push and pop are always O(1) β€” no traversal, no shifting
LIFO order is structurally enforced
β–Ά
Naturally models function calls, undo history, and bracket matching
No index access, no search
β–Ά
The constraint is the feature β€” misuse is impossible by design
Backed by a resizable array (ArrayDeque)
β–Ά
No linked-list pointer overhead β€” cache-friendly, memory-efficient
Empty stack has no top element
β–Ά
Always check isEmpty() before peek() or pop() β€” both throw on empty
03
Section Three Β· Operations

Core Operations

Push
Places a new element on top of the stack, increasing the size by one in O(1) time.
Pseudocode
function push(stack, value): stack.addFirst(value) // O(1) β€” moves top pointer up // amortized O(1) if backing array resizes
Push β€” add 40 to top of stack
BEFORE 30 top β†’ 20 10 push(40) AFTER 40 top β†’ 30 20 10 β‘  new element lands on top
ArrayDeque.push() vs addFirst():
  • Deque<Integer> stack = new ArrayDeque<>() β€” then call push() which is an alias for addFirst().
  • Both are O(1). Avoid add() which appends to the tail β€” the wrong end for stack semantics.
Pop
Removes and returns the top element of the stack, decreasing the size by one in O(1) time.
Pseudocode
function pop(stack): if stack is empty: throw EmptyStackException // guard β€” always check first return stack.removeFirst() // O(1) β€” moves top pointer down
Pop β€” remove top element from stack
BEFORE 40 top β†’ 30 20 10 pop() AFTER 40 β†’ returns 40 30 top β†’ 20 10
Pop throws β€” always guard with isEmpty():
  • Calling pop() or removeFirst() on an empty ArrayDeque throws NoSuchElementException at runtime β€” no compile-time warning.
  • The pattern is always: if (!stack.isEmpty()) stack.pop(). For null instead of an exception, use pollFirst().
Peek
Returns the top element without removing it, allowing inspection of the stack's current state in O(1) time.
Pseudocode
function peek(stack): if stack is empty: throw EmptyStackException // guard return stack.peekFirst() // O(1) β€” reads without removing
peek() vs peekFirst() vs element():
  • On ArrayDeque used as a stack: peekFirst() returns null if empty, element() throws if empty.
  • Use peekFirst() when an empty stack is a valid state your code should handle; use element() when empty means a bug and you want to fail loudly.
isEmpty
Returns true if the stack contains no elements, preventing pop() and peek() from throwing on an empty stack.
Pseudocode
function isEmpty(stack): return stack.size() == 0 // O(1) β€” size is a stored field
Guard every pop and peek:
  • isEmpty() is not optional ceremony β€” it is the mandatory guard before every pop() and peek().
  • In interview code, missing this check is an instant red flag. Write the guard first, then the operation.
Reverse a Collection
Uses a stack's LIFO property to reverse the order of any sequence by pushing all elements then popping them back.
Pseudocode
function reverse(array): stack = new Stack for each element in array: push(stack, element) // push all β€” O(n) result = new Array[array.length] for i from 0 to array.length - 1: result[i] = pop(stack) // pop all β€” O(n) return result // O(n) total, O(n) space
Reverse β€” push all, pop all
INPUT 1 2 3 4 5 push all β†’ STACK 5 top 4 3 2 1 ← pop all OUTPUT 5 4 3 2 1 reversed!
LIFO reversal is the canonical stack use case:
  • Any time you need to process items in reverse order without knowing the total count upfront, push them all and pop them back.
  • This pattern also underlies iterative DFS, the call stack simulation in tree traversal, and undo/redo systems.
Common Mistake: Using the legacy Stack class from java.util. Stack extends Vector, which synchronizes every method β€” push, pop, peek, and isEmpty all acquire a lock on every call, even in single-threaded code. The correct replacement is Deque<T> stack = new ArrayDeque<>() β€” same interface, no lock overhead, and it does not expose get(index) or other methods that violate stack semantics.
04
Section Four Β· Implementation

Build It Once

Build this from scratch once β€” it makes the mechanics concrete. In any real project or interview, reach for ArrayDeque (Section 05).

Java β€” Stack core mechanics
// Build once to understand the mechanics β€” use ArrayDeque in practice. public class Stack { private int[] data; private int top; // index of the top element, -1 when empty public Stack(int capacity) { data = new int[capacity]; top = -1; } // O(1) β€” place value at top, move pointer up public void push(int value) { if (top == data.length - 1) resize(); data[++top] = value; // increment first, then write } // O(1) β€” read value at top, move pointer down public int pop() { if (top == -1) throw new RuntimeException("empty"); return data[top--]; // read first, then decrement } // O(1) β€” read without modifying the pointer public int peek() { if (top == -1) throw new RuntimeException("empty"); return data[top]; } }
05
Section Five Β· Java Built-in

Use It In Java

IN JAVA
Use Deque<T> stack = new ArrayDeque<>()
Import import java.util.ArrayDeque; import java.util.Deque;
Method Complexity Note
push(value) O(1) amort Alias for addFirst() β€” adds to top
pop() O(1) Removes + returns top; throws if empty
peek() O(1) Returns top without removing; null if empty
pollFirst() O(1) Like pop() but returns null instead of throwing
peekFirst() O(1) Like peek() β€” prefer for null safety
isEmpty() O(1) Always call before pop() or peek()
size() O(1) Number of elements currently in stack
⚠ Gotcha: Deque has two ends β€” push() and pop() operate on the HEAD (front), which is the top of the stack. If you accidentally call addLast() or removeLast() you are operating on the wrong end and your stack is silently broken. Always use push/pop/peek or addFirst/removeFirst/peekFirst β€” never the Last variants on a stack.
Choose Use ArrayDeque when you need a stack. It is backed by a resizable array with no synchronization overhead. Use LinkedList only if you are already using it for other operations and want to avoid a second object β€” ArrayDeque is faster in all pure stack use cases.
06
Section Six Β· Decision Guide

Pick The Right Structure

Decision Flowchart
Need LIFO β€” last in, first out? Yes No Single-threaded / perf critical? Yes ArrayDeque<T> push/pop O(1), no lock overhead No Legacy Stack<T> ⚠ avoid β€” synchronized overhead Need FIFO instead? Yes ArrayDeque as Queue addLast / removeFirst No ArrayList random access needed
Comparison Table
Structure Push/Pop Peek Thread-safe Verdict
ArrayDeque as Stack ← this O(1) O(1) No (use intentionally) Correct choice
Legacy Stack class O(1) O(1) Yes (synchronized) Avoid β€” lock overhead
LinkedList as Stack O(1) O(1) No Works, cache-unfriendly
ArrayList as Stack O(1) amort O(1) No Works (get last), non-idiomatic
07
Section Seven Β· Practice

Problems To Solve

Two patterns unlock most stack interview problems: monotonic stack (maintaining a stack where elements are always in sorted order β€” increasing or decreasing β€” to find next greater/smaller elements efficiently) and nested structure unwinding (using the stack to match opening and closing delimiters, or to reverse/decode nested sequences). Look for problem descriptions containing "next greater", "previous smaller", "valid brackets", "decode", or "evaluate expression" β€” they signal stack-based solutions.

Difficulty Pattern Problem Key Insight
Easy monotonic Valid Parentheses Push every opening bracket onto the stack β€” when you encounter a closing bracket, the top must be its exact matching opener or the string is invalid.
Medium monotonic Min Stack Maintain a second parallel stack that tracks the running minimum β€” push to it only when the new value is less than or equal to the current minimum top.
Medium monotonic Daily Temperatures A monotonic decreasing stack of indices β€” when you find a warmer day, pop indices and record the gap between the current index and the popped index.
Medium monotonic Basic Calculator II Push numbers onto the stack; when you see * or /, pop and evaluate immediately β€” defer + and - until the end by pushing signed values.
Medium reversal Decode String Two stacks β€” one for repeat counts, one for the string built so far β€” let you cleanly unwind nested brackets without recursion.
Hard monotonic Largest Rectangle in Histogram A monotonic increasing stack of indices tells you, for each bar, exactly how far left it can extend as the shortest bar in a potential rectangle.
Interview Pattern:
  • The monotonic stack solves roughly 60% of stack interview problems β€” any time a problem asks for the "next greater element", "nearest smaller value", or "maximum rectangle", think monotonic stack first.
  • The second pattern β€” using a stack to match or unwind nested structure β€” covers bracket validation, expression evaluation, and string decoding.
  • Recognise the pattern and the solution structure follows immediately.

β†’ See all Stack problems with full solutions