From Theory to Mastery
[!NOTE] This module explores the core principles of Stacks & Queues Practice Problems, deriving solutions from first principles and hardware constraints to build world-class, production-ready expertise.
You know push and pop. Now let’s solve problems that appear in Google, Amazon, and Meta interviews.
1. Daily Temperatures
Problem: Given [73, 74, 75, 71, 69, 72, 76, 73]. Return [1, 1, 4, 2, 1, 1, 0, 0].
(How many days until a warmer temperature?)
Intuition: Monotonic Stack
We want the Next Greater Element. Use a Decreasing Monotonic Stack storing indices.
- If
current > top, we found a warmer day fortop. Pop and calculate distance.
Solutions
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] res = new int[n];
Deque<Integer> stack = new ArrayDeque<>(); // Indices
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
int prevIndex = stack.pop();
res[prevIndex] = i - prevIndex; // Distance
}
stack.push(i);
}
return res;
}
func dailyTemperatures(temperatures []int) []int {
n := len(temperatures)
res := make([]int, n)
stack := []int{} // Indices
for i, t := range temperatures {
for len(stack) > 0 && t > temperatures[stack[len(stack)-1]] {
idx := stack[len(stack)-1]
stack = stack[:len(stack)-1]
res[idx] = i - idx
}
stack = append(stack, i)
}
return res;
}
2. Largest Rectangle in Histogram
Problem: Given heights [2, 1, 5, 6, 2, 3]. Find the area of the largest rectangle.
Intuition
This is the Next Smaller Element (and Previous Smaller Element) problem! We can do this in one pass with a Monotonic Increasing Stack.
- Pop Condition: When current height is less than stack top. The popped bar is taller than current.
- Area:
h * (i - new_top - 1).
Interactive Analysis
Watch how the stack calculates area.
- Push index if height is increasing.
- Pop if height decreases. Calculate area for popped bar.
Histogram [2, 1, 5, 6, 2, 3]
Stack (Indices)
Solutions
public int largestRectangleArea(int[] heights) {
Deque<Integer> stack = new ArrayDeque<>();
stack.push(-1); // Sentinel for left boundary
int maxArea = 0;
for (int i = 0; i < heights.length; i++) {
while (stack.peek() != -1 && heights[i] <= heights[stack.peek()]) {
int h = heights[stack.pop()];
int w = i - stack.peek() - 1; // Right - Left - 1
maxArea = Math.max(maxArea, h * w);
}
stack.push(i);
}
// Clean up remaining stack
while (stack.peek() != -1) {
int h = heights[stack.pop()];
int w = heights.length - stack.peek() - 1;
maxArea = Math.max(maxArea, h * w);
}
return maxArea;
}
func largestRectangleArea(heights []int) int {
stack := []int{-1} // Sentinel
maxArea := 0
for i, h := range heights {
for len(stack) > 1 && h <= heights[stack[len(stack)-1]] {
height := heights[stack[len(stack)-1]]
stack = stack[:len(stack)-1]
width := i - stack[len(stack)-1] - 1
if area := height * width; area > maxArea {
maxArea = area
}
}
stack = append(stack, i)
}
// Remaining
for len(stack) > 1 {
height := heights[stack[len(stack)-1]]
stack = stack[:len(stack)-1]
width := len(heights) - stack[len(stack)-1] - 1
if area := height * width; area > maxArea {
maxArea = area
}
}
return maxArea;
}
Complexity
| Strategy | Time | Space | Notes |
|---|---|---|---|
| Monotonic Stack | O(N) | O(N) | One pass. |
3. Implement Queue using Stacks
Problem: Implement a FIFO Queue using only two LIFO Stacks.
Intuition
- Push (
s1): Incoming. - Pop (
s2): Outgoing. Ifs2empty, pours1intos2. This reverses order (LIFO + LIFO = FIFO).
Solutions
class MyQueue {
Deque<Integer> in = new ArrayDeque<>();
Deque<Integer> out = new ArrayDeque<>();
public void push(int x) {
in.push(x);
}
public int pop() {
peek(); // Ensure out has elements
return out.pop();
}
public int peek() {
if (out.isEmpty()) {
while (!in.isEmpty()) {
out.push(in.pop());
}
}
return out.peek();
}
}
type MyQueue struct {
in, out []int
}
func (this *MyQueue) Push(x int) {
this.in = append(this.in, x)
}
func (this *MyQueue) Pop() int {
this.Peek()
val := this.out[len(this.out)-1]
this.out = this.out[:len(this.out)-1]
return val
}
func (this *MyQueue) Peek() int {
if len(this.out) == 0 {
for len(this.in) > 0 {
val := this.in[len(this.in)-1]
this.in = this.in[:len(this.in)-1]
this.out = append(this.out, val)
}
}
return this.out[len(this.out)-1]
}