Progress
🌍
Why does this topic matter?
Stacks and queues are the two most fundamental sequential data structures after arrays and linked lists. Every operating system uses a call stack to manage function invocations — that's why you see "stack overflow" when recursion goes too deep. Every web browser uses a history stack for the back button. Every customer service system uses queues. In interview problems, the monotonic stack pattern alone appears in dozens of FAANG questions — Next Greater Element, Daily Temperatures, Largest Rectangle in Histogram, Trapping Rain Water. The deque (double-ended queue) unlocks O(n) solutions to sliding window min/max problems that would otherwise require O(n log n) with a heap. These are not just data structures — they are thinking tools.

📖 Before We Start — The Mental Model

Think of a stack as a pile of plates — you can only add or remove from the top (Last In, First Out = LIFO). Think of a queue as a line at a coffee shop — first person in line is first to be served (First In, First Out = FIFO).

These simple behaviours unlock surprisingly powerful patterns: you can use a stack to reverse order, to match pairs, and to track history. You can use a queue to process things level by level — which is exactly what BFS does on graphs and trees.

🔗
How it connects: Lecture 11 (Linked Lists) showed you how to build singly/doubly linked lists. A stack is just a linked list where you only touch the head. A queue is a linked list where you add at the tail and remove from the head. Lecture 15 (Trees) uses stacks for DFS traversal and queues for BFS level-order traversal — you'll use everything from this lecture there.

📚 Stack Fundamentals — LIFO

🎯
Core Property: Last In, First Out (LIFO). Only the top element is accessible. Three operations — push(x) (add to top), pop() (remove top), peek() (read top without removing) — all O(1).
Stack visualisation — push(1), push(2), push(3): After push(1): [1] ← top After push(2): [2] ← top [1] After push(3): [3] ← top [2] [1] pop() → returns 3. Stack: [2][1] peek()→ returns 2. Stack unchanged: [2][1]

Stack Implementations in Java

Implementation Backed By push pop Notes
ArrayDeque<T> (preferred) Resizable array O(1)* O(1) Fastest, no synchronisation overhead
Stack<T> (legacy) Vector O(1)* O(1) Synchronized — avoid in single-threaded code
LinkedList (as Deque) Doubly linked list O(1) O(1) More GC pressure from node allocation
☕ Java · Stack using ArrayDeque (Gold Standard)
// ✅ PREFERRED — ArrayDeque used as a stack
Deque<Integer> stack = new ArrayDeque<>();

stack.push(10);          // addFirst(10) — add to top
stack.push(20);
stack.push(30);

stack.peek();            // 30 — read top, do NOT remove
stack.pop();             // 30 — removes and returns top
stack.isEmpty();         // false
stack.size();            // 2

// ❌ AVOID in interviews — legacy, synchronized
Stack<Integer> legacyStack = new Stack<>();
legacyStack.push(10);   // works, but prefer Deque

Java Stack API Quick Reference

☕ Java · Deque as Stack — All Key Methods
// Push: addFirst / push / offerFirst
stack.push(x);          // throws if capacity exceeded (rare)
stack.offerFirst(x);    // returns false if capacity exceeded

// Pop: removeFirst / pop / pollFirst
stack.pop();             // throws NoSuchElementException if empty
stack.pollFirst();       // returns null if empty — safer for conditionals

// Peek: peekFirst / peek / getFirst
stack.peek();            // null if empty
stack.getFirst();        // throws if empty

// Check
stack.isEmpty();
stack.size();

// ⚠️ Common interview pattern — check before pop:
if (!stack.isEmpty() && stack.peek() < threshold)
    stack.pop();

📈 Monotonic Stack — The FAANG Power Pattern

🎯
What is a Monotonic Stack? A stack that is always kept in a sorted order (either monotonically increasing or decreasing). When a new element arrives that violates the order, we pop elements until the order is restored. Each popped element has "found its answer" — the new element is the answer it was waiting for.

Next Greater Element Pattern

For each element, find the first element to its right that is greater than it. Brute force is O(n²). Monotonic stack solves it in O(n).

Array: [2, 1, 5, 3, 4] NGE: [5, 5, -1, 4, -1] Walk left→right. Maintain a DECREASING stack of indices. i=0, val=2: stack empty → push 0. stack=[0] (vals:[2]) i=1, val=1: 1 ≤ 2 → push 1. stack=[0,1] (vals:[2,1]) i=2, val=5: 5>1 → pop 1, ans[1]=5 5>2 → pop 0, ans[0]=5 stack empty → push 2. stack=[2] (vals:[5]) i=3, val=3: 3 ≤ 5 → push 3. stack=[2,3] (vals:[5,3]) i=4, val=4: 4>3 → pop 3, ans[3]=4 4 ≤ 5 → push 4. stack=[2,4] (vals:[5,4]) End: remaining on stack → NGE = -1. ans[2]=-1, ans[4]=-1 Result: [5, 5, -1, 4, -1] ✓
☕ Java · Next Greater Element — Monotonic Decreasing Stack
public int[] nextGreaterElement(int[] nums) {
    int[] ans = new int[nums.length];
    Arrays.fill(ans, -1);                    // default: no greater element
    Deque<Integer> stack = new ArrayDeque<>(); // stores INDICES

    for (int i = 0; i < nums.length; i++) {
        // Pop all indices whose element is smaller than current
        while (!stack.isEmpty() && nums[stack.peek()] < nums[i]) {
            ans[stack.pop()] = nums[i]; // current is the NGE for popped index
        }
        stack.push(i);
    }
    return ans;
}
// Time: O(N) — each element pushed/popped at most once
// Space: O(N) — stack in worst case

Largest Rectangle in Histogram

Find the largest rectangle that fits inside a histogram. Each bar has width 1. This classic problem uses a stack to track left and right boundaries for each bar height.

Heights: [2, 1, 5, 6, 2, 3] ┌─┐ │6│ ┌─┼─┼─┐ │5│6│ │ │ │ │ │ ┌─┐ ┌─┐ │ │ │2│ │3│ │2│ │ │ │ │ │ │ ┗━┷━━┷━┷━┷━┷━━━┷━┛ Largest rectangle = 10 units (heights 5 and 6, width 2) Key insight: For bar at index i with height h[i], - Left boundary: first bar to the left that is SHORTER than h[i] - Right boundary: first bar to the right that is SHORTER than h[i] Stack maintains indices in INCREASING height order. When a shorter bar arrives, pop — each popped bar found its right boundary.
☕ Java · Largest Rectangle in Histogram — Monotonic Increasing Stack
public int largestRectangleArea(int[] heights) {
    Deque<Integer> stack = new ArrayDeque<>(); // indices, increasing heights
    int maxArea = 0, n = heights.length;

    for (int i = 0; i <= n; i++) {
        // Use 0 as sentinel at end to flush all remaining bars
        int curH = (i == n) ? 0 : heights[i];
        while (!stack.isEmpty() && curH < heights[stack.peek()]) {
            int h = heights[stack.pop()];          // height of this bar
            int w = stack.isEmpty() ? i : i - stack.peek() - 1; // width
            maxArea = Math.max(maxArea, h * w);
        }
        stack.push(i);
    }
    return maxArea;
}
// [2,1,5,6,2,3]:
// i=0: push 0. stack=[0]
// i=1: h[1]=1 < h[0]=2 → pop 0, h=2, w=1(stack empty), area=2
//      push 1. stack=[1]
// i=2,3: increasing → push. stack=[1,2,3]
// i=4: h=2 < h[3]=6 → pop 3, h=6,w=4-2-1=1, area=6
//      2 < h[2]=5 → pop 2, h=5,w=4-1-1=2, area=10 ← max!
// ... continues. Final maxArea = 10 ✓

🚶 Queue Fundamentals — FIFO

📌
Core Property: First In, First Out (FIFO). Elements enter at the tail (rear) and exit from the head (front). Operations: offer(x) (enqueue), poll() (dequeue), peek() — all O(1).

Queue Implementations in Java

Implementation Use Case Notes
ArrayDeque<T> (preferred) General FIFO Fastest, no null elements
LinkedList<T> When null needed Slower due to GC
PriorityQueue<T> Min/Max ordering NOT FIFO — ordering by priority
ArrayBlockingQueue<T> Thread-safe bounded For multi-threaded use
☕ Java · Queue using ArrayDeque (FIFO)
// ArrayDeque used as a QUEUE — add at tail, remove from head
Queue<Integer> queue = new ArrayDeque<>();

queue.offer(1);         // enqueue to tail
queue.offer(2);
queue.offer(3);
                         // queue: [1, 2, 3]  (1 is head)
queue.peek();           // → 1  (read head, no remove)
queue.poll();           // → 1  (remove head)
queue.peek();           // → 2
queue.size();           // → 2
queue.isEmpty();        // → false

// BFS template — bread-and-butter queue usage:
while (!queue.isEmpty()) {
    int node = queue.poll();
    // process node
    // add neighbours: queue.offer(neighbour)
}

Deque — Sliding Window Maximum

A deque (double-ended queue) allows O(1) add/remove from both ends. This enables a powerful pattern: keep a monotonically decreasing deque of indices for an O(n) sliding window maximum solution.

nums=[1,3,-1,-3,5,3,6,7], k=3 → max per window Windows: [1,3,-1]→3, [3,-1,-3]→3, [-1,-3,5]→5, [-3,5,3]→5, [5,3,6]→6, [3,6,7]→7 Deque (monotonically decreasing, stores indices): i=0, val=1: deque empty → addLast(0). deque=[0] (vals:[1]) i=1, val=3: 3>nums[0]=1 → removeLast, addLast(1). deque=[1] (vals:[3]) i=2, val=-1:-1≤3→ addLast(2). deque=[1,2] (vals:[3,-1]) Window [0..2] done → ans[0]=nums[deque.peek()]=nums[1]=3 ✓ i=3, val=-3: -3≤-1→ addLast(3). deque=[1,2,3](vals:[3,-1,-3]) i-deque.peek()=3-1=2=k-1 → deque[0] expired (i-k+1=1, dq[0]=1≥1 ok) Wait: 3-k+1=1 ≤ deque.peek()=1 → still valid. ans[1]=3 ✓ ... and so on
☕ Java · Sliding Window Maximum — O(n) Deque
public int[] maxSlidingWindow(int[] nums, int k) {
    int[] res = new int[nums.length - k + 1];
    Deque<Integer> dq = new ArrayDeque<>(); // stores indices, decreasing values

    for (int i = 0; i < nums.length; i++) {
        // 1. Remove indices outside the current window
        if (!dq.isEmpty() && dq.peekFirst() < i - k + 1)
            dq.pollFirst();

        // 2. Remove indices with smaller values (they can never be max)
        while (!dq.isEmpty() && nums[dq.peekLast()] < nums[i])
            dq.pollLast();

        dq.offerLast(i);

        // 3. Window is full — record the max (front of deque)
        if (i >= k - 1) res[i - k + 1] = nums[dq.peekFirst()];
    }
    return res;
}
// Each element enters and leaves the deque at most once → O(N) total

🔢 Expression Evaluation — Stack Arithmetic

Evaluating arithmetic expressions is a classic stack application. The key idea: numbers go onto an operand stack; operators control when to compute. Reverse Polish Notation (RPN / postfix) eliminates the need for parentheses — operators come after their operands.

RPN: ["2","1","+","3","*"] → (2+1)*3 = 9 Process token by token: "2" → push 2. stack=[2] "1" → push 1. stack=[2,1] "+" → pop 1, pop 2 → 2+1=3 → push 3. stack=[3] "3" → push 3. stack=[3,3] "*" → pop 3, pop 3 → 3*3=9 → push 9. stack=[9] Result = 9 ✓
☕ Java · Evaluate Reverse Polish Notation
public int evalRPN(String[] tokens) {
    Deque<Integer> stack = new ArrayDeque<>();
    for (String t : tokens) {
        if ("+-*/".contains(t)) {
            int b = stack.pop(), a = stack.pop(); // order matters!
            switch (t) {
                case "+": stack.push(a + b); break;
                case "-": stack.push(a - b); break;
                case "*": stack.push(a * b); break;
                case "/": stack.push(a / b); break; // truncates toward zero
            }
        } else {
            stack.push(Integer.parseInt(t));
        }
    }
    return stack.pop();
}

🗺 Pattern Decision Guide

Balanced brackets / undo history → Plain Stack
Next greater/smaller, span problems → Monotonic Decreasing Stack
Histogram area, trapping rain water → Monotonic Increasing Stack
BFS / level-order traversal → Queue
Sliding window max/min in O(n) → Monotonic Deque
Priority scheduling, top-K → PriorityQueue (Heap)
Stack using queues / Queue using stacks → Design (two data structures)


💪 Practice Problems

Problem 01 · Plain Stack
Valid Parentheses
Easy GoogleMeta Stack
Problem 02 · Design
Min Stack
Medium AmazonGoogle Two-Stack Design
Problem 03 · Monotonic Stack
Daily Temperatures
Medium AmazonGoogle Monotonic Stack
Problem 04 · Monotonic Stack + HashMap
Next Greater Element I
Easy Amazon Mono Stack + HashMap
Problem 05 · Monotonic Increasing Stack
Largest Rectangle in Histogram
Hard AmazonGoogle Monotonic Stack
Problem 06 · Monotonic Deque
Sliding Window Maximum
Hard GoogleAmazon Monotonic Deque
Problem 07 · Stack Arithmetic
Evaluate Reverse Polish Notation
Medium AmazonLinkedIn Stack
Problem 08 · Circular Queue Design
Design Circular Queue
Medium MicrosoftAmazon Array + Two Pointers

📝 Assignment

📋
Topic 12 Assignment — 25 Problems
Complete the assignment before moving to HashMap & HashSet. Includes stack design problems, monotonic stack variants (Stock Span, Trapping Rain Water), and BFS via Queue problems.

📄 Open Assignment →

✅ Lecture Completion Checklist

I can implement a stack using ArrayDeque and know why to prefer it over Stack<T>
I can solve Valid Parentheses from memory in under 3 minutes
I understand the Monotonic Stack pattern and can identify when to use increasing vs decreasing
I can solve Daily Temperatures (Next Greater Element pattern) in O(N)
I understand how the width calculation works in Largest Rectangle in Histogram
I can implement a Deque-based sliding window maximum solution in O(N)
I know the difference between Queue.offer() vs add() and Queue.poll() vs remove()
I can evaluate RPN expressions using a stack
I can design a circular queue with wrap-around using modulo arithmetic
I can use the Pattern Decision Guide to choose the right Stack/Queue variant for a problem
🧠
You're ready for Topic 13: HashMap & HashSet Deep Dive
You've mastered LIFO/FIFO, monotonic patterns, and deque tricks. HashMap is the most used data structure in FAANG interviews — it underlies complement lookup, frequency counting, prefix sums, and grouping. Everything from here gets faster with a HashMap.
← Topic 11: Linked Lists Topic 13: HashMap & HashSet →