Pattern family: Stack / Monotonic Stack / Design / Expression Evaluation Notes & Templates: 01_stack_notes.md Language: Java
- P1 β Next Greater Element I
- P2 β Daily Temperatures
- P3 β Next Greater Element II (Circular)
- P4 β Previous Greater Element / Stock Span
- P5 β Online Stock Span
- P6 β Largest Rectangle in Histogram
- P7 β Trapping Rain Water
- P8 β Sum of Subarray Minimums
- P9 β Remove Nodes From Linked List
- P10 β Valid Parentheses
- P11 β Minimum Remove to Make Valid
- P12 β Longest Valid Parentheses
- P13 β Score of Parentheses
- P14 β Remove All Adjacent Duplicates I
- P15 β Remove All Adjacent Duplicates II
- P16 β Backspace String Compare
- P17 β Removing Stars From a String
- P18 β Remove Duplicate Letters
- P19 β Remove K Digits
- P20 β Evaluate Reverse Polish Notation
- P21 β Basic Calculator II
- P22 β Basic Calculator
- P23 β Decode String
- P24 β Simplify Path
- P25 β Asteroid Collision
- P26 β Baseball Game
- P27 β Reverse a String Using Stack
- P28 β Min Stack
- P29 β Implement Queue Using Stacks
- P30 β Implement Stack Using Queues
- P31 β Design Browser History
- P32 β Maximum Frequency Stack
LeetCode #496 | Difficulty: Easy | Company: Amazon, Google | Category: Monotonic Stack
Find the next greater element for each element of
nums1innums2. Return -1 if none exists.
Build a monotonic decreasing stack on nums2. Store results in a HashMap. For each element in nums1, look up in the map.
| Step | Condition | Action |
|---|---|---|
| Loop nums2 | st not empty && nums2[top] < nums2[i] |
map[nums2[pop]] = nums2[i] |
| Push | always | st.push(i) |
| Answer | for each num in nums1 | map.getOrDefault(num, -1) |
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
Map<Integer, Integer> map = new HashMap<>();
Deque<Integer> st = new ArrayDeque<>();
for (int num : nums2) {
while (!st.isEmpty() && st.peek() < num)
map.put(st.pop(), num); // num is the next greater for popped element
st.push(num); // push value (nums2 has unique elements)
}
int[] res = new int[nums1.length];
for (int i = 0; i < nums1.length; i++)
res[i] = map.getOrDefault(nums1[i], -1);
return res;
}nums1 = [4,1,2], nums2 = [1,3,4,2]
Monotonic stack on nums2:
push 1 β st=[1]
3 > 1 β map[1]=3, push 3 β st=[3]
4 > 3 β map[3]=4, push 4 β st=[4]
2 < 4 β push 2 β st=[4,2]
Remaining: no next greater β not in map
nums1: 4 β not in map β -1
1 β map[1]=3
2 β not in map β -1
Output: [-1, 3, -1] β
LeetCode #739 | Difficulty: Medium | Company: Amazon, Facebook | Category: Monotonic Stack
For each day, find how many days until a warmer temperature. Return 0 if no warmer day exists.
Same as Next Greater but record the distance (index difference), not the value.
| Step | Condition | Action |
|---|---|---|
| Loop LβR | temp[top] < temp[i] |
result[pop] = i - pop (distance) |
| Push | always | st.push(i) (index) |
| Default | no warmer day | result[i] = 0 |
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] result = new int[n]; // default 0
Deque<Integer> st = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
while (!st.isEmpty() && temperatures[st.peek()] < temperatures[i]) {
int idx = st.pop();
result[idx] = i - idx; // distance to warmer day
}
st.push(i);
}
return result;
}temperatures = [73,74,75,71,69,72,76,73]
i=0(73): st=[0]
i=1(74): 73<74 β result[0]=1-0=1. st=[1]
i=2(75): 74<75 β result[1]=2-1=1. st=[2]
i=3(71): 71<75 β push. st=[2,3]
i=4(69): push. st=[2,3,4]
i=5(72): 69<72 β result[4]=1, 71<72 β result[3]=2. st=[2,5]
i=6(76): 72<76 β result[5]=1, 75<76 β result[2]=4. st=[6]
i=7(73): push. st=[6,7] (remain: result stays 0)
Output: [1,1,4,2,1,1,0,0] β
LeetCode #503 | Difficulty: Medium | Company: Amazon | Category: Monotonic Stack Circular
Same as NGE but the array is circular β wrap around to find greater element.
Loop 2*n times. Use i % n for actual index. Push to stack only in the first pass (i < n).
public int[] nextGreaterElements(int[] nums) {
int n = nums.length;
int[] result = new int[n];
Arrays.fill(result, -1);
Deque<Integer> st = new ArrayDeque<>();
for (int i = 0; i < 2 * n; i++) {
while (!st.isEmpty() && nums[st.peek()] < nums[i % n])
result[st.pop()] = nums[i % n];
if (i < n) st.push(i); // push only first pass
}
return result;
}nums = [1, 2, 1]
i=0: st=[0]
i=1: 1<2 β result[0]=2. st=[1]
i=2: 2>1 β push. st=[1,2]
i=3(β1): 1<2 already done. st=[1,2]
i=4(β2): 1<2 β result[2]=2. st=[1]
i=5(β1): done.
Output: [2,-1,2] β
LeetCode #901 | Difficulty: Medium | Category: Monotonic Stack
For each element, find how many consecutive elements (including itself) to its left are β€ current.
Loop LβR. Pop elements β€ current (they're dominated). Span = i - top_index (or i+1 if stack empty).
public int[] stockSpan(int[] prices) {
int n = prices.length;
int[] result = new int[n];
Deque<Integer> st = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
while (!st.isEmpty() && prices[st.peek()] <= prices[i])
st.pop();
result[i] = st.isEmpty() ? i + 1 : i - st.peek();
st.push(i);
}
return result;
}prices = [100, 80, 60, 70, 60, 75, 85]
i=0(100): empty β span=1. st=[0]
i=1(80): 100>80 β span=1. st=[0,1]
i=2(60): 80>60 β span=1. st=[0,1,2]
i=3(70): 60<=70 β pop 2. 80>70 β span=3-1=2. st=[0,1,3]
i=4(60): 70>60 β span=1. st=[0,1,3,4]
i=5(75): 60<=75 β pop 4. 70<=75 β pop 3. 80>75 β span=5-1=4. st=[0,1,5]
i=6(85): 75<=85 β pop 5. 80<=85 β pop 1. 100>85 β span=6-0=6. st=[0,6]
Output: [1,1,1,2,1,4,6] β
LeetCode #901 | Difficulty: Medium | Company: Amazon, Google | Category: Monotonic Stack Design
Design a system where prices arrive one at a time and return the span for each incoming price.
No index available. Store [price, span] in stack. When popping, accumulate the span of the popped element.
class StockSpanner {
Deque<int[]> st = new ArrayDeque<>(); // [price, span]
public int next(int price) {
int span = 1;
while (!st.isEmpty() && st.peek()[0] <= price)
span += st.pop()[1]; // accumulate span of dominated days
st.push(new int[]{price, span});
return span;
}
}next(100) β st=[(100,1)], span=1
next(80) β st=[(100,1),(80,1)], span=1
next(60) β st=[...,(60,1)], span=1
next(70) β pop (60,1): span=1+1=2. st=[...,(80,1),(70,2)], span=2
next(60) β span=1
next(75) β pop (60,1)+pop(70,2): span=1+1+2=4. pop (80,1): span=5? β no, 80>75 β stop. span=4
next(85) β pop (75,4)+(80,1): span=6.
Output: [1,1,1,2,1,4,6] β
LeetCode #84 | Difficulty: Hard | Company: Amazon, Google, Microsoft | Category: Monotonic Stack
Given histogram bar heights, find the largest rectangle area.
When a shorter bar arrives, all taller bars to its left cannot extend further right. Pop them and compute width using i - newTop - 1. Sentinel 0 at end forces all remaining bars to be processed.
| Step | Condition | Action |
|---|---|---|
Loop 0 to n (n = sentinel) |
arr[top] > h |
area = h_pop Γ (i - newTop - 1) |
| Sentinel | i == n |
h = 0 forces all pops |
| Push | always | push index i |
public int largestRectangleArea(int[] heights) {
int n = heights.length;
Deque<Integer> st = new ArrayDeque<>();
st.push(-1); // sentinel for width calculation
int maxArea = 0;
for (int i = 0; i <= n; i++) {
int h = (i == n) ? 0 : heights[i];
while (st.peek() != -1 && heights[st.peek()] > h) {
int height = heights[st.pop()];
int width = i - st.peek() - 1;
maxArea = Math.max(maxArea, height * width);
}
st.push(i);
}
return maxArea;
}heights = [2,1,5,6,2,3]
sentinel=-1 in stack.
i=0(2): push. st=[-1,0]
i=1(1): 2>1 β pop 0: h=2, w=1-(-1)-1=1, area=2. push 1. st=[-1,1]
i=2(5): push. st=[-1,1,2]
i=3(6): push. st=[-1,1,2,3]
i=4(2): 6>2 β pop 3: h=6, w=4-2-1=1, area=6
5>2 β pop 2: h=5, w=4-1-1=2, area=10 β max
1<2 β stop. push 4. st=[-1,1,4]
i=5(3): push. st=[-1,1,4,5]
i=6(0): 3>0 β pop 5: h=3, w=6-4-1=1, area=3
2>0 β pop 4: h=2, w=6-1-1=4, area=8
1>0 β pop 1: h=1, w=6-(-1)-1=6, area=6
Output: 10 β
LeetCode #42 | Difficulty: Hard | Company: Amazon, Google, Facebook | Category: Monotonic Stack
Given elevation heights, compute how much water can be trapped.
Popped element = valley bottom. New stack top = left wall. Current index = right wall. Water = (min(left, right) - bottom) Γ width.
public int trap(int[] height) {
int n = height.length;
Deque<Integer> st = new ArrayDeque<>();
int water = 0;
for (int i = 0; i < n; i++) {
while (!st.isEmpty() && height[st.peek()] < height[i]) {
int bottom = height[st.pop()];
if (st.isEmpty()) break;
int left = height[st.peek()];
int right = height[i];
int width = i - st.peek() - 1;
water += (Math.min(left, right) - bottom) * width;
}
st.push(i);
}
return water;
}height = [0,1,0,2,1,0,1,3,2,1,2,1]
i=2(0): push
i=3(2): pop 2(0): bottom=0, left=1, right=2, w=3-1-1=1, water+=1
pop 1(1): bottom=1, left empty β break
...
Output: 6 β
LeetCode #907 | Difficulty: Medium | Company: Amazon | Category: Monotonic Stack Contribution
For every subarray, sum all their minimums. Return result modulo 1e9+7.
For each element at index idx, calculate how many subarrays it is the minimum of:
left= distance to previous smaller (or boundary)right= distance to next smaller (or boundary)- Contribution =
arr[idx] Γ left Γ right
Use > (not >=) on one side to avoid double counting equal elements.
public int sumSubarrayMins(int[] arr) {
int n = arr.length, MOD = (int) 1e9 + 7;
Deque<Integer> st = new ArrayDeque<>();
long result = 0;
for (int i = 0; i <= n; i++) {
int curr = (i == n) ? 0 : arr[i];
while (!st.isEmpty() && arr[st.peek()] > curr) {
int idx = st.pop();
int left = idx - (st.isEmpty() ? -1 : st.peek()); // prev smaller boundary
int right = i - idx; // next smaller boundary
result = (result + (long) arr[idx] * left * right) % MOD;
}
st.push(i);
}
return (int) result;
}arr = [3, 1, 2, 4]
i=1(1): pop 0(3): left=0-(-1)=1, right=1-0=1, contrib=3Γ1Γ1=3
i=4(0): pop 3(4): left=3-2=1, right=4-3=1, contrib=4
pop 2(2): left=2-1=1, right=4-2=2, contrib=4
pop 1(1): left=1-(-1)=2, right=4-1=3, contrib=6
Total = 3+1+4+4+6-1 = 17 (careful: arr[1]=1 contributes 1Γ2Γ3=6 not -1)
Output: 17 β ([3]=3,[1]=1,[2]=2,[4]=4,[3,1]=1,[1,2]=1,[2,4]=2,[3,1,2]=1,[1,2,4]=1,[3,1,2,4]=1 β sum=17)
LeetCode #2487 | Difficulty: Medium | Company: Amazon | Category: Monotonic Stack
Remove every node that has a node with a greater value to its right.
Convert list to array, apply monotonic decreasing stack (keep only nodes where no greater element exists to the right), rebuild list.
public ListNode removeNodes(ListNode head) {
List<Integer> vals = new ArrayList<>();
for (ListNode cur = head; cur != null; cur = cur.next)
vals.add(cur.val);
Deque<Integer> st = new ArrayDeque<>();
for (int v : vals) {
while (!st.isEmpty() && st.peek() < v) st.pop();
st.push(v);
}
// Rebuild from stack (stack is reversed)
List<Integer> res = new ArrayList<>(st);
Collections.reverse(res);
ListNode dummy = new ListNode(0), cur = dummy;
for (int v : res) { cur.next = new ListNode(v); cur = cur.next; }
return dummy.next;
}list = 5β2β13β3β8
vals = [5,2,13,3,8]
Monotonic decreasing stack:
push 5 β [5]
2<5 β push [5,2]? No: 5>2 so push [5,2] β wait, we pop smaller:
actually: push 5; 2<5 push; 13>2 pop 2, 13>5 pop 5, push 13; 3<13 push; 8>3 pop 3, 8<13 push 8
st = [13, 8] β reversed = [13, 8]? Wrong.
Correct trace (pop when top < new val):
5: st=[5]
2: 2<5 β push. st=[5,2]
13: 2<13 pop, 5<13 pop β st=[13]
3: 3<13 β push. st=[13,3]
8: 3<8 pop β st=[13,8]
Reversed: [13,8] β but 5 should also remain if nothing greater is to its right...
Hmm: 5 has 13 to its right (greater) β removed. Correct.
Output: 13β8 β
LeetCode #20 | Difficulty: Easy | Company: Amazon, Google, Facebook | Category: Bracket Matching
Determine if a string of brackets is valid (every open bracket has a matching close bracket in the correct order).
| Step | Condition | Action |
|---|---|---|
| Open bracket | (, {, [ |
push |
| Close bracket | stack empty | return false |
| Close bracket | top mismatches | return false |
| Close bracket | top matches | pop |
| End | stack empty | return true |
public boolean isValid(String s) {
Deque<Character> st = new ArrayDeque<>();
for (char ch : s.toCharArray()) {
if (ch == '(' || ch == '{' || ch == '[') {
st.push(ch);
} else {
if (st.isEmpty()) return false;
char top = st.pop();
if (ch == ')' && top != '(') return false;
if (ch == '}' && top != '{') return false;
if (ch == ']' && top != '[') return false;
}
}
return st.isEmpty();
}s = "()[]{}" β push (, pop+match ), push [, pop+match ], push {, pop+match } β empty β true β
s = "([)]" β push (, push [, ) vs [ β mismatch β false β
LeetCode #1249 | Difficulty: Medium | Company: Facebook, Amazon | Category: Bracket Matching
Remove the minimum number of parentheses to make the string valid. Return any valid result.
public String minRemoveToMakeValid(String s) {
Set<Integer> toRemove = new HashSet<>();
Deque<Integer> st = new ArrayDeque<>(); // stores indices of unmatched '('
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (ch == '(') st.push(i);
else if (ch == ')') {
if (st.isEmpty()) toRemove.add(i); // unmatched ')'
else st.pop(); // matched pair
}
}
while (!st.isEmpty()) toRemove.add(st.pop()); // remaining '(' are unmatched
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++)
if (!toRemove.contains(i)) sb.append(s.charAt(i));
return sb.toString();
}s = "lee(t(c)o)de)"
i=3 '(': st=[3]
i=5 '(': st=[3,5]
i=7 ')': pop 5. st=[3]
i=9 ')': pop 3. st=[]
i=12 ')': empty β toRemove={12}
Output: "lee(t(c)o)de" β
LeetCode #32 | Difficulty: Hard | Company: Amazon, Google | Category: Bracket Matching
Find the length of the longest valid (well-formed) parentheses substring.
Stack stores indices. Push -1 as a base sentinel. On ( push index. On ): pop β if stack empty push current index as new base, else update max length.
public int longestValidParentheses(String s) {
Deque<Integer> st = new ArrayDeque<>();
st.push(-1); // base sentinel
int maxLen = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {
st.push(i);
} else {
st.pop();
if (st.isEmpty()) st.push(i); // new base
else maxLen = Math.max(maxLen, i - st.peek());
}
}
return maxLen;
}s = ")()())"
st=[-1]
i=0 ')': pop -1, empty β push 0. st=[0]
i=1 '(': push 1. st=[0,1]
i=2 ')': pop 1. len=2-0=2. st=[0]
i=3 '(': push 3. st=[0,3]
i=4 ')': pop 3. len=4-0=4. maxLen=4
i=5 ')': pop 0, empty β push 5. st=[5]
Output: 4 β
LeetCode #856 | Difficulty: Medium | Company: Google | Category: Bracket Score
()= 1,(X)= 2ΓX, concatenation = addition. Return the score of the balanced string.
public int scoreOfParentheses(String s) {
Deque<Integer> st = new ArrayDeque<>();
st.push(0); // score at current depth
for (char ch : s.toCharArray()) {
if (ch == '(') {
st.push(0); // new depth level, score starts at 0
} else {
int top = st.pop();
int val = top == 0 ? 1 : 2 * top; // () = 1; (X) = 2X
st.push(st.pop() + val); // add to parent level
}
}
return st.pop();
}s = "(()(()))"
push 0 β [0]
push 0 β [0,0] (first '(')
push 0 β [0,0,0] (second '(')
')' β top=0 β val=1, parent: [0, 0+1=1]
push 0 β [0,1,0]
push 0 β [0,1,0,0]
')' β top=0 β val=1, [0,1,0+1=1]
')' β top=1 β val=2, [0,1+2=3]
')' β top=3 β val=6, [0+6=6]
Output: 6 β
LeetCode #1047 | Difficulty: Easy | Company: Amazon | Category: Stack Removal
Repeatedly remove adjacent duplicate pairs until no more can be removed.
public String removeDuplicates(String s) {
Deque<Character> st = new ArrayDeque<>();
for (char ch : s.toCharArray()) {
if (!st.isEmpty() && st.peek() == ch) st.pop(); // duplicate β remove pair
else st.push(ch);
}
StringBuilder sb = new StringBuilder();
while (!st.isEmpty()) sb.append(st.pop());
return sb.reverse().toString();
}s = "abbaca"
a β [a]
b β [a,b]
b == top β pop β [a]
a == top β pop β []
c β [c]
a β [c,a]
Output: "ca" β
LeetCode #1209 | Difficulty: Medium | Company: Amazon, Google | Category: Stack + Count
Remove k adjacent duplicate characters repeatedly.
Store [char, count] pairs. When count reaches k, pop the entire group. Cascading is automatic.
public String removeDuplicates(String s, int k) {
Deque<int[]> st = new ArrayDeque<>(); // [charCode, count]
for (char ch : s.toCharArray()) {
if (!st.isEmpty() && st.peek()[0] == ch) {
st.peek()[1]++;
if (st.peek()[1] == k) st.pop();
} else {
st.push(new int[]{ch, 1});
}
}
StringBuilder sb = new StringBuilder();
List<int[]> list = new ArrayList<>(st);
Collections.reverse(list);
for (int[] pair : list)
for (int i = 0; i < pair[1]; i++)
sb.append((char) pair[0]);
return sb.toString();
}s = "deeedbbcccbdaa", k = 3
dβ[(d,1)] eβ[(e,1),(d,1)] eβ[(e,2),(d,1)] eβcount=3βPOPβ[(d,1)]
dβ[(d,2)] bβ[(b,1),(d,2)] bβ[(b,2),(d,2)]
cβ[(c,1),(b,2),(d,2)] cβ[(c,2)] cβcount=3βPOPβ[(b,2),(d,2)]
bβ[(b,3)] count=3βPOPβ[(d,2)] dβ[(d,3)]βPOPβ[]
aβ[(a,1)] aβ[(a,2)]
Output: "aa" β
LeetCode #844 | Difficulty: Easy | Company: Amazon, Facebook | Category: Stack Simulation
Compare two strings where
#means backspace. Return if they are equal.
public boolean backspaceCompare(String s, String t) {
return process(s).equals(process(t));
}
private String process(String s) {
Deque<Character> st = new ArrayDeque<>();
for (char ch : s.toCharArray()) {
if (ch == '#') { if (!st.isEmpty()) st.pop(); }
else st.push(ch);
}
StringBuilder sb = new StringBuilder();
while (!st.isEmpty()) sb.append(st.pop());
return sb.reverse().toString();
}s = "ab#c" β process: aβ[a] bβ[a,b] #β[a] cβ[a,c] β "ac"
t = "ad#c" β process: aβ[a] dβ[a,d] #β[a] cβ[a,c] β "ac"
Output: true β
LeetCode #2390 | Difficulty: Medium | Company: Amazon | Category: Stack Simulation
*removes the closest non-star character to its left.
public String removeStars(String s) {
Deque<Character> st = new ArrayDeque<>();
for (char ch : s.toCharArray()) {
if (ch == '*') st.pop();
else st.push(ch);
}
StringBuilder sb = new StringBuilder();
while (!st.isEmpty()) sb.append(st.pop());
return sb.reverse().toString();
}s = "leet**cod*e"
lβ[l] eβ[l,e] eβ[l,e,e] tβ[l,e,e,t] *β[l,e,e] *β[l,e] cβ[l,e,c] oβ[l,e,c,o] dβ[l,e,c,o,d] *β[l,e,c,o] eβ[l,e,c,o,e]
Output: "lecoe" β
LeetCode #316 | Difficulty: Hard | Company: Google, Amazon | Category: Monotonic Stack + Greedy
Remove duplicate letters so each letter appears once and the result is the lexicographically smallest.
Greedy: maintain a monotonic increasing stack of characters. Pop a larger character only if it appears again later. Use visited[] to skip already-in-stack chars.
public String removeDuplicateLetters(String s) {
int[] lastIdx = new int[26];
boolean[] visited = new boolean[26];
for (int i = 0; i < s.length(); i++)
lastIdx[s.charAt(i) - 'a'] = i;
Deque<Character> st = new ArrayDeque<>();
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (visited[ch - 'a']) continue; // already in result
while (!st.isEmpty()
&& st.peek() > ch
&& lastIdx[st.peek() - 'a'] > i) { // can remove (appears later)
visited[st.pop() - 'a'] = false;
}
st.push(ch);
visited[ch - 'a'] = true;
}
StringBuilder sb = new StringBuilder();
while (!st.isEmpty()) sb.append(st.pop());
return sb.reverse().toString();
}s = "bcabc"
lastIdx: b=3, c=4, a=2
i=0(b): push b. st=[b], visited={b}
i=1(c): push c. st=[b,c], visited={b,c}
i=2(a): c>a and lastIdx[c]=4>2 β pop c, unvisit c
b>a and lastIdx[b]=3>2 β pop b, unvisit b
push a. st=[a], visited={a}
i=3(b): push b. st=[a,b]
i=4(c): push c. st=[a,b,c]
Output: "abc" β
LeetCode #402 | Difficulty: Medium | Company: Google, Amazon | Category: Monotonic Stack + Greedy
Remove k digits from a number to form the smallest possible number.
Maintain a monotonic increasing stack. Pop whenever top > current AND k > 0. If k still remains after the loop, remove from the back (largest digits). Handle leading zeros.
| Step | Condition | Action |
|---|---|---|
| Each digit | k>0 && top > curr |
pop + k-- |
| Push | always | push char |
| After loop | k > 0 |
pollLast() k times |
| Rebuild | skip leading zeros | start appending after first non-zero |
public String removeKdigits(String num, int k) {
Deque<Character> st = new ArrayDeque<>();
for (char ch : num.toCharArray()) {
while (k > 0 && !st.isEmpty() && st.peek() > ch) {
st.pollLast(); // pop from back (top of stack)
k--;
}
st.addLast(ch); // push to back
}
while (k-- > 0) st.pollLast(); // remove largest remaining digits
StringBuilder sb = new StringBuilder();
boolean leadingZero = true;
for (char c : st) {
if (leadingZero && c == '0') continue;
leadingZero = false;
sb.append(c);
}
return sb.length() == 0 ? "0" : sb.toString();
}num = "1432219", k = 3
1β[1] 4β[1,4] 3: 4>3,k=2 β pop 4 β [1,3] 2: 3>2,k=1βpop 3β[1,2] 2: pushβ[1,2,2]
2: pushβ[1,2,2,2] 1: 2>1,k=0βpop 2,k=0βstop β [1,2,2,1]? wait k=0 after 1 pop...
Correct trace: k=3
1β[1] 4β[1,4] 3: pop 4(k=2)β[1,3] 2: pop 3(k=1)β[1,2] 2: pushβ[1,2,2]
2: pushβ[1,2,2,2] 1: pop 2(k=0)β[1,2,2,1]? k=0 so stop, push 1β[1,2,2,1]?
wait: 2>1 and k=1βpop, k=0. push 1. β [1,2,2] then k=0 so push 1,9.
Output: "1219" β
LeetCode #150 | Difficulty: Medium | Company: Amazon, LinkedIn | Category: Expression Stack
Evaluate an expression in Reverse Polish Notation (postfix). Operands pushed; operators pop 2, compute, push result.
| Token | Action |
|---|---|
| Number | st.push(Integer.parseInt(token)) |
| Operator | b = pop, a = pop, push(a op b) |
public int evalRPN(String[] tokens) {
Deque<Integer> st = new ArrayDeque<>();
for (String token : tokens) {
if ("+-*/".contains(token)) {
int b = st.pop(); // right operand (more recent)
int a = st.pop(); // left operand
switch (token) {
case "+" -> st.push(a + b);
case "-" -> st.push(a - b);
case "*" -> st.push(a * b);
case "/" -> st.push(a / b);
}
} else {
st.push(Integer.parseInt(token));
}
}
return st.pop();
}tokens = ["2","1","+","3","*"]
push 2 β [2]
push 1 β [2,1]
"+": b=1, a=2, push 3 β [3]
push 3 β [3,3]
"*": b=3, a=3, push 9 β [9]
Output: 9 (= (2+1)*3) β
LeetCode #227 | Difficulty: Medium | Company: Facebook, Amazon | Category: Expression Stack
Evaluate a string expression with
+,-,*,/(no parentheses).
Track the last operator (sign). For +/-: push sign * num. For *//: pop top, compute with num, push result. Sum the stack at end.
public int calculate(String s) {
Deque<Integer> st = new ArrayDeque<>();
int num = 0;
char sign = '+'; // last seen operator
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (Character.isDigit(ch)) num = num * 10 + (ch - '0');
if ((!Character.isDigit(ch) && ch != ' ') || i == s.length() - 1) {
switch (sign) {
case '+' -> st.push(num);
case '-' -> st.push(-num);
case '*' -> st.push(st.pop() * num);
case '/' -> st.push(st.pop() / num);
}
sign = ch;
num = 0;
}
}
int result = 0;
while (!st.isEmpty()) result += st.pop();
return result;
}s = "3+2*2"
ch='3': num=3
ch='+': sign was '+' β push 3. sign='+', num=0
ch='2': num=2
ch='*': sign was '+' β push 2. sign='*', num=0
ch='2': num=2, last char
sign='*' β pop 2, push 2*2=4. stack=[3,4]
sum = 3 + 4 = 7 β
LeetCode #224 | Difficulty: Hard | Company: Amazon, Google | Category: Expression Stack
Evaluate a string with
+,-, and parentheses. Handles nested parentheses.
Push current result and sign onto stack when ( is encountered. Restore when ) is found.
public int calculate(String s) {
Deque<Integer> st = new ArrayDeque<>();
int result = 0, num = 0, sign = 1;
for (char ch : s.toCharArray()) {
if (Character.isDigit(ch)) {
num = num * 10 + (ch - '0');
} else if (ch == '+') {
result += sign * num; num = 0; sign = 1;
} else if (ch == '-') {
result += sign * num; num = 0; sign = -1;
} else if (ch == '(') {
st.push(result); // save current result
st.push(sign); // save sign before '('
result = 0; sign = 1;
} else if (ch == ')') {
result += sign * num; num = 0;
result *= st.pop(); // multiply by sign before '('
result += st.pop(); // add saved result
}
}
return result + sign * num;
}s = "1 + (2 - (3 + 1))"
result=0, sign=1
'1': num=1
'+': result=1, num=0, sign=1
'(': push 1, push 1. result=0, sign=1
'2': num=2
'-': result=2, num=0, sign=-1
'(': push 2, push -1. result=0, sign=1
'3': num=3
'+': result=3, sign=1
'1': num=1
')': result=3+1=4, result*=pop(-1)=-4, result+=pop(2)=-2
')': result=-2+0=result*=pop(1)=-2, result+=pop(1)=-1
Output: -1 β (1 + (2 - 4) = 1 + (-2) = -1)
LeetCode #394 | Difficulty: Medium | Company: Google, Amazon | Category: Save/Restore State
3[abc]βabcabcabc. Supports nesting:2[3[a]]βaaaaaa.
| Token | Action |
|---|---|
| Digit | k = k*10 + digit (handle multi-digit) |
[ |
push (k, curr) onto stacks; reset k=0, curr="" |
] |
prev = pop; curr = prev + curr Γ times |
| Letter | append to curr |
public String decodeString(String s) {
Deque<Integer> counts = new ArrayDeque<>();
Deque<StringBuilder> strings = new ArrayDeque<>();
StringBuilder curr = new StringBuilder();
int k = 0;
for (char ch : s.toCharArray()) {
if (Character.isDigit(ch)) {
k = k * 10 + (ch - '0');
} else if (ch == '[') {
counts.push(k);
strings.push(curr);
curr = new StringBuilder();
k = 0;
} else if (ch == ']') {
int times = counts.pop();
StringBuilder prev = strings.pop();
for (int i = 0; i < times; i++) prev.append(curr);
curr = prev;
} else {
curr.append(ch);
}
}
return curr.toString();
}s = "3[a2[c]]"
'3': k=3
'[': push (3, ""), curr="", k=0
'a': curr="a"
'2': k=2
'[': push (2, "a"), curr="", k=0
'c': curr="c"
']': times=2, prev="a" β prev="a"+"c"+"c"="acc". curr="acc"
']': times=3, prev="" β prev=""+"acc"+"acc"+"acc"="accaccacc". curr="accaccacc"
Output: "accaccacc" β
LeetCode #71 | Difficulty: Medium | Company: Facebook, Amazon | Category: Stack Simulation
Convert a Unix file path to its canonical (simplified) form.
| Token | Action |
|---|---|
"" or "." |
skip |
".." |
pop if stack not empty |
| anything else | push directory name |
public String simplifyPath(String path) {
Deque<String> st = new ArrayDeque<>();
for (String dir : path.split("/")) {
if (dir.equals("..")) {
if (!st.isEmpty()) st.pop();
} else if (!dir.isEmpty() && !dir.equals(".")) {
st.push(dir);
}
}
List<String> list = new ArrayList<>(st);
Collections.reverse(list);
StringBuilder sb = new StringBuilder();
for (String d : list) sb.append("/").append(d);
return sb.length() == 0 ? "/" : sb.toString();
}path = "/home/../usr/./bin"
Split: ["", "home", "..", "usr", ".", "bin"]
"": skip
"home": push β [home]
"..": pop β []
"usr": push β [usr]
".": skip
"bin": push β [usr, bin]
Output: "/usr/bin" β
LeetCode #735 | Difficulty: Medium | Company: Amazon, Apple | Category: Conditional Stack
Positive asteroids move right, negative move left. When they collide, smaller explodes. Equal-size both explode.
| Condition | Action |
|---|---|
| Same direction (top>0,curr>0) or (top<0,curr<0) | no collision, push |
| curr > 0 | always push (moving right, no left asteroid coming) |
| curr < 0, top > 0 | collision! larger survives |
public int[] asteroidCollision(int[] asteroids) {
Deque<Integer> st = new ArrayDeque<>();
for (int a : asteroids) {
boolean alive = true;
while (alive && a < 0 && !st.isEmpty() && st.peek() > 0) {
if (st.peek() < -a) st.pop(); // stack asteroid explodes
else if (st.peek() == -a) { st.pop(); alive = false; } // both explode
else alive = false; // incoming explodes
}
if (alive) st.push(a);
}
int[] res = new int[st.size()];
for (int i = res.length - 1; i >= 0; i--) res[i] = st.pop();
return res;
}asteroids = [5, 10, -5]
push 5 β [5]
push 10 β [5,10]
-5: 10 > 5 β alive=false (incoming -5 explodes)
push nothing.
Output: [5,10] β
asteroids = [8,-8]
push 8 β [8]
-8: 8 == 8 β both explode. st=[]
Output: [] β
LeetCode #682 | Difficulty: Easy | Company: Amazon | Category: Stack Simulation
Simulate a baseball scoring game.
"+"= sum of last 2,"D"= double last,"C"= cancel last.
public int calPoints(String[] operations) {
Deque<Integer> st = new ArrayDeque<>();
for (String op : operations) {
switch (op) {
case "+" -> st.push(st.peek() + ((int[]) st.toArray())[1]); // top + second
case "D" -> st.push(st.peek() * 2);
case "C" -> st.pop();
default -> st.push(Integer.parseInt(op));
}
}
// simpler "+" implementation:
int sum = 0;
while (!st.isEmpty()) sum += st.pop();
return sum;
}public int calPoints(String[] ops) {
Deque<Integer> st = new ArrayDeque<>();
for (String op : ops) {
if (op.equals("C")) {
st.pop();
} else if (op.equals("D")) {
st.push(st.peek() * 2);
} else if (op.equals("+")) {
int top = st.pop();
int newTop = top + st.peek();
st.push(top);
st.push(newTop);
} else {
st.push(Integer.parseInt(op));
}
}
int sum = 0;
while (!st.isEmpty()) sum += st.pop();
return sum;
}ops = ["5","2","C","D","+"]
push 5 β [5]
push 2 β [5,2]
C β pop β [5]
D β push 10 β [5,10]
+ β top=10, peek=5, newTop=15 β [5,10,15]
sum = 5+10+15 = 30 β
Difficulty: Easy | Category: Basic Stack
Reverse a string using a stack.
public String reverseString(String s) {
Deque<Character> st = new ArrayDeque<>();
for (char ch : s.toCharArray()) st.push(ch);
StringBuilder sb = new StringBuilder();
while (!st.isEmpty()) sb.append(st.pop());
return sb.toString();
}s = "hello"
Push: h,e,l,l,o β stack top = 'o'
Pop order: o,l,l,e,h
Output: "olleh" β
LeetCode #155 | Difficulty: Medium | Company: Amazon, Google, Facebook | Category: Augmented Stack
Design a stack that supports
push,pop,top, andgetMinβ all in O(1) time.
Store [value, minSoFar] pairs. getMin() = peek at the second element of the top pair. No extra stack needed.
class MinStack {
Deque<int[]> st = new ArrayDeque<>(); // [value, minSoFar]
public void push(int val) {
int min = st.isEmpty() ? val : Math.min(val, st.peek()[1]);
st.push(new int[]{val, min});
}
public void pop() { st.pop(); }
public int top() { return st.peek()[0]; }
public int getMin() { return st.peek()[1]; } // O(1) always
}push(-2) β st=[(-2,-2)]
push(0) β min=min(0,-2)=-2 β st=[(-2,-2),(0,-2)]
push(-3) β min=min(-3,-2)=-3 β st=[...,(0,-2),(-3,-3)]
getMin() β peek()[1] = -3 β
pop() β st=[(-2,-2),(0,-2)]
top() β peek()[0] = 0 β
getMin() β peek()[1] = -2 β
LeetCode #232 | Difficulty: Easy | Company: Amazon, Microsoft | Category: Two Stacks
Implement a FIFO queue using only two stacks.
inbox for push. outbox for pop/peek. Transfer from inbox to outbox only when outbox is empty. Each element transferred at most once β amortized O(1) per operation.
class MyQueue {
Deque<Integer> inbox = new ArrayDeque<>();
Deque<Integer> outbox = new ArrayDeque<>();
public void push(int x) { inbox.push(x); }
public int pop() { transfer(); return outbox.pop(); }
public int peek() { transfer(); return outbox.peek(); }
public boolean empty() { return inbox.isEmpty() && outbox.isEmpty(); }
private void transfer() {
if (outbox.isEmpty())
while (!inbox.isEmpty()) outbox.push(inbox.pop());
}
}push(1): inbox=[1]
push(2): inbox=[2,1]
peek(): outbox empty β transfer β outbox=[1,2]. peek=1 β
pop(): outbox=[1,2], pop=1. outbox=[2]
push(3): inbox=[3]
pop(): outbox=[2], pop=2. outbox=[]
pop(): outbox empty β transfer β outbox=[3]. pop=3 β
LeetCode #225 | Difficulty: Easy | Company: Amazon | Category: Two Queues / One Queue
Implement a LIFO stack using only queue operations.
After each push, rotate the queue so the newly pushed element comes to the front. Then pop = poll() directly.
class MyStack {
Queue<Integer> q = new LinkedList<>();
public void push(int x) {
q.offer(x);
for (int i = 0; i < q.size() - 1; i++)
q.offer(q.poll()); // rotate: bring x to front
}
public int pop() { return q.poll(); }
public int top() { return q.peek(); }
public boolean empty() { return q.isEmpty(); }
}push(1): q=[1]
push(2): q=[2,1] after rotate (2 at front)
push(3): q=[3,2,1] after rotate (3 at front)
top(): peek = 3 β
pop(): poll = 3. q=[2,1]
top(): peek = 2 β
LeetCode #1472 | Difficulty: Medium | Company: Amazon, Google | Category: Two Stacks
Design a browser with
visit,back(steps), andforward(steps)operations.
Two stacks: back for visited pages, forward for pages after current. On visit, clear forward. On back/forward, move between stacks step by step.
class BrowserHistory {
Deque<String> back = new ArrayDeque<>();
Deque<String> forward = new ArrayDeque<>();
String curr;
public BrowserHistory(String homepage) { curr = homepage; }
public void visit(String url) {
back.push(curr);
curr = url;
forward.clear(); // clear forward history on new visit
}
public String back(int steps) {
while (steps-- > 0 && !back.isEmpty()) {
forward.push(curr);
curr = back.pop();
}
return curr;
}
public String forward(int steps) {
while (steps-- > 0 && !forward.isEmpty()) {
back.push(curr);
curr = forward.pop();
}
return curr;
}
}BrowserHistory("leetcode.com")
visit("google.com"): back=[leetcode], curr=google, forward=[]
visit("facebook.com"): back=[google,leetcode], curr=facebook, forward=[]
visit("youtube.com"): back=[facebook,google,leetcode], curr=youtube, forward=[]
back(1): forward=[youtube], curr=facebook β
back(1): forward=[youtube,facebook], curr=google β
forward(1): back=[...,google], curr=facebook β
visit("linkedin.com"): forward=[] cleared! curr=linkedin β
forward(2): forward empty β curr=linkedin β
back(2): curr=facebook β curr=google β
back(7): go as far as possible β curr=leetcode β
LeetCode #895 | Difficulty: Hard | Company: Google, Amazon | Category: Design Stack
Design a stack-like data structure.
push(x)adds x.pop()removes the most frequent element (ties broken by most recently pushed).
Track freq[x] = frequency of x. Track maxFreq = current max frequency. Track group[f] = list of elements pushed when they first reached frequency f. On pop, remove from group[maxFreq], decrement freq, decrement maxFreq if group becomes empty.
class FreqStack {
Map<Integer, Integer> freq = new HashMap<>();
Map<Integer, Deque<Integer>> group = new HashMap<>();
int maxFreq = 0;
public void push(int val) {
int f = freq.merge(val, 1, Integer::sum);
maxFreq = Math.max(maxFreq, f);
group.computeIfAbsent(f, k -> new ArrayDeque<>()).push(val);
}
public int pop() {
Deque<Integer> top = group.get(maxFreq);
int val = top.pop();
freq.merge(val, -1, Integer::sum);
if (top.isEmpty()) { group.remove(maxFreq); maxFreq--; }
return val;
}
}push(5): freq={5:1}, group={1:[5]}, maxFreq=1
push(7): freq={5:1,7:1}, group={1:[7,5]}, maxFreq=1
push(5): freq={5:2,7:1}, group={1:[7,5],2:[5]}, maxFreq=2
push(7): freq={5:2,7:2}, group={1:[7,5],2:[7,5]}, maxFreq=2
push(4): freq={5:2,7:2,4:1}, group={1:[4,7,5],2:[7,5]}, maxFreq=2
push(5): freq={5:3,...}, group={...3:[5]}, maxFreq=3
pop(): maxFreq=3 β pop 5 from group[3]. freq[5]=2. group[3] empty β maxFreq=2. return 5 β
pop(): maxFreq=2 β pop 7 from group[2]. freq[7]=1. return 7 β
pop(): maxFreq=2 β pop 5 from group[2]. freq[5]=1. return 5 β
pop(): maxFreq=1 β pop 4 from group[1]. return 4 β
| Problem | Category | Key technique |
|---|---|---|
| Next Greater Element I | Monotonic β | pop when top < curr; HashMap for lookup |
| Daily Temperatures | Monotonic β | pop when top < curr; record distance |
| NGE II Circular | Monotonic β 2n | i % n; push only first n |
| Stock Span | Monotonic β | i - top before push |
| Online Stock Span | Monotonic design | push [price, span]; accumulate on pop |
| Largest Rectangle | Monotonic β + sentinel | area = h Γ (i - newTop - 1) |
| Trapping Rain Water | Monotonic valley | water = (min(L,R) - bottom) Γ width |
| Sum Subarray Mins | Monotonic contribution | val Γ left Γ right per element |
| Remove Nodes LL | Monotonic β | keep only nodes with no greater to right |
| Valid Parentheses | Bracket match | push open; match on close |
| Min Remove Valid | Bracket index | store indices; remove unmatched set |
| Longest Valid Parens | Bracket + sentinel | -1 base; i - peek on match |
| Score of Parentheses | Bracket depth | push 0 on (; compute on ) |
| Remove Adjacent I | Stack removal | pop on same char |
| Remove Adjacent II | Stack + count | [char,count]; pop when count==k |
| Backspace Compare | Stack simulation | pop on # |
| Remove Stars | Stack simulation | pop on * |
| Remove Duplicate Letters | Monotonic + greedy | pop if top > curr AND appears later |
| Remove K Digits | Monotonic + greedy | pop if top > curr AND k > 0 |
| Evaluate RPN | Expression stack | b=pop, a=pop; push(a op b) |
| Basic Calculator II | Expression + sign | push with sign; */ inline |
| Basic Calculator | Expression + parens | push (result, sign) on (; restore on ) |
| Decode String | Save/restore state | push (k, sb) on [; repeat on ] |
| Simplify Path | Stack simulation | split /; push dirs; pop on .. |
| Asteroid Collision | Conditional stack | pop while right>0 and left<0 |
| Baseball Game | Stack simulation | C=pop, D=Γ2, +=sum of top 2 |
| Reverse String | Basic stack | push all, pop all |
| Min Stack | Augmented stack | push (val, min); getMin = peek()[1] |
| Queue via Stacks | Two stacks | inbox + outbox; transfer on empty |
| Stack via Queues | One queue rotate | rotate after each push |
| Browser History | Two stacks | back + forward; clear forward on visit |
| Max Frequency Stack | Freq + group map | group[freq] = deque; pop from maxFreq |