Master this topic with zero to advance depth.
A Stack is a linear data structure that rigidly follows the Last-In, First-Out (LIFO) principle. It represents a constraint on how we access data: we can only interact with the top of the stack. Think of it like a stack of plates or a history of actions.
A properly implemented stack guarantees operations.
| Operation | Description | Time Complexity | Space Complexity |
|---|---|---|---|
Push(x) | Add element x to the top. | total | |
Pop() | Remove and return the top element. | ||
Peek() / Top() | Return the top element without removing it. | ||
IsEmpty() | Check if the stack has no elements. |
Stacks are the perfect tool when processing data requires remembering past but unresolved information in the reverse order of their arrival.
There is a deep correspondence between Stacks and Recursion. Every recursive algorithm can be implemented iteratively using an explicit stack.
StackOverflowError.Object or Array in heaps, you can often handle deeper levels of nesting and have more control over the state.A Monotonic Stack is a regular stack where elements are forced to remain sorted (either strictly increasing or strictly decreasing). It's a magical pattern that turns brute-force searches into solutions.
Maintain the monotonic property by popping elements that violate the rule before pushing the new element.
Goal: Find the Next Greater Element for [2, 1, 2, 4, 3]
Constraint: Stack must be Monotonic Decreasing (store unresolved elements)
1. Process '2': Stack [2]
2. Process '1': 1 < 2, so keep it. Stack [2, 1]
3. Process '2': 2 > 1. Violated!
- Pop '1'. The next greater for '1' is '2'.
- Push '2'. Stack [2, 2]
4. Process '4': 4 > 2. Violated!
- Pop '2'. Next greater is '4'.
- Pop '2'. Next greater is '4'.
- Push '4'. Stack [4]
5. Process '3': 3 < 4, so keep it. Stack [4, 3][!TIP] Monotonic Decreasing Stack: Use to find the Next Greater Element. Monotonic Increasing Stack: Use to find the Next Smaller Element (or previous smaller).
Valid Parentheses
Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.
An input string is valid if:
s = "{ [ ] }"
Step 1: '{' -> Push to Stack. Stack: ['{']
Step 2: '[' -> Push to Stack. Stack: ['{', '[']
Step 3: ']' -> Matches Top '['. Pop. Stack: ['{']
Step 4: '}' -> Matches Top '{'. Pop. Stack: []
Result: Valid (Stack is Empty)Examples
Intuition
Regular expressions or string replacements can continuously remove valid adjacent pairs (), {}, [] until the string is empty or no more replacements can be made.
Intuition
Instead of an explicit stack object, we can use recursion. Each function call 'expects' a specific closing bracket. If it finds one, it returns; if it finds another opening bracket, it recurses. This demonstrates how the computer's memory uses a stack internally.
Intuition
Iterate through the string. If we see an opening bracket, push its corresponding closing bracket onto the stack. If we see a closing bracket, check if it matches the top of the stack. This simplifies the logic by storing what we expect next.
Detailed Dry Run
| Char | Action | Stack Status | Match? |
|---|---|---|---|
| '(' | Push ')' | [')'] | - |
| '[' | Push ']' | [')', ']'] | - |
| ']' | Pop | [')'] | Yes |
| ')' | Pop | [] | Yes |
⚠️ Common Pitfalls & Tips
Be careful with strings that only contain opening brackets, or strings where the stack becomes empty too early.
Min Stack
Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.
Push: -2, 0, -3
Stack: MinStack:
| -3 | | -3 |
| 0 | | -2 |
| -2 | | -2 |
+----+ +----+
getMin() -> -3
pop()
top() -> 0
getMin() -> -2Examples
Intuition
Use a standard stack for push, pop, and top. For getMin(), iterate over the entire stack to find the minimum value. This saves space over a two-stack approach but sacrifices the constant-time operation for querying the minimum.
Intuition
Instead of two separate stacks, we can store pairs on a single stack. Each entry on the stack will be (current_value, current_min_so_far). This is conceptually simpler and ensures the value and its corresponding minimum are always synchronized.
Intuition
Maintain two stacks: one for all elements and another to keep track of the minimum at each state. When pushing a value, if it is less than or equal to the current minimum, push it onto the min stack as well. This ensures O(1) retrieval.
Detailed Dry Run
| Operation | Value | Stack | MinStack | Output |
|---|---|---|---|---|
| push | -2 | [-2] | [-2] | - |
| push | 0 | [-2, 0] | [-2] | - |
| push | -3 | [-2, 0, -3] | [-2, -3] | - |
| getMin | - | - | - | -3 |
| pop | - | [-2, 0] | [-2] | - |
⚠️ Common Pitfalls & Tips
In Java, use .equals() instead of == for Stack comparisons as Integer objects might be outside the cache range.
Implement Queue using Stacks
Implement a first in first out (FIFO) queue using only two his stacks. The implemented queue should support all the functions of a normal queue (push, peek, pop, and empty).
Examples
Intuition
To make a stack behave like a queue, the oldest entered elements must be kept at the top of the stack. For every newly pushed element, we can move all elements to a temporary stack, push the new element to the bottom, and move everything back.
Intuition
We can implement the 'pop' or 'peek' operation using a single stack and recursion. The function call stack acts as the second stack. To pop the bottom element, we pop the top, recurse to the bottom, and then push the popped element back as we return. This is less efficient than two stacks but insightful.
Use an input stack to handle pushes and an output stack for peeks/pops. When the output stack is empty, transfer all elements from input to output. This reverses the order to FIFO.
Detailed Dry Run
| Operation | Value | in_stack | out_stack | Output |
|---|---|---|---|---|
| push | 1 | [1] | [] | - |
| push | 2 | [1, 2] | [] | - |
| peek | - | [] | [2, 1] | 1 |
| pop | - | [] | [2] | 1 |
| empty | - | [] | [2] | false |
Implement Stack using Queues
Implement a last-in-first-out (LIFO) stack using only two queues. The implemented stack should support all the functions of a normal stack (push, top, pop, and empty).
Examples
Intuition
When a new element arrives, enqueue it to a secondary empty queue. Then, dequeue all elements from the main queue and enqueue them after the new element. Finally, swap the main and secondary queues. The newest element is now at the front.
When pushing an element, add it to the queue, and then rotate the queue (pop and re-add) N-1 times so the newly added element is now at the front. This avoids needing a second queue temporarily.
Detailed Dry Run
| Operation | Value | Queue State | Action |
|---|---|---|---|
| push | 1 | [1] | Add 1 |
| push | 2 | [1, 2] -> [2, 1] | Add 2, rotate 1 element |
| pop | - | [1] | Pop front (2) |
| empty | - | [1] | Check if empty |
Evaluate Reverse Polish Notation
Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are +, -, *, and /.
Tokens: ["2", "1", "+", "3", "*"]
1. Token "2": Push. Stack: [2]
2. Token "1": Push. Stack: [2, 1]
3. Token "+": Pop 1, Pop 2. Res = 2 + 1 = 3. Push. Stack: [3]
4. Token "3": Push. Stack: [3, 3]
5. Token "*": Pop 3, Pop 3. Res = 3 * 3 = 9. Push. Stack: [9]
Result: 9Examples
((2 + 1) * 3) = 9
Intuition
Without knowing the stack trick, one naive approach is to scan the token list for an operator, evaluate the two preceding operands, replace those three tokens with the result, and repeat until one token remains. This is simpler to reason about but requires repeated passes over the list.
Intuition
Use a stack to store operands. When an operator is encountered, pop the top two operands, apply the operator, and push the result back. This naturally handles the postfix order without parentheses.
Detailed Dry Run
| Token | Action | Stack Status | Calculated |
|---|---|---|---|
| "2" | Push | [2] | - |
| "1" | Push | [2, 1] | - |
| "+" | Pop, Pop, Add | [3] | 2 + 1 = 3 |
| "3" | Push | [3, 3] | - |
| "*" | Pop, Pop, Mul | [9] | 3 * 3 = 9 |
⚠️ Common Pitfalls & Tips
Be mindful of integer division in Python (int(a/b)) and JavaScript (Math.trunc(a/b)), as they must truncate toward zero.
Daily Temperatures
Given an array of integers temperatures, return an array answer such that answer[i] is the number of days you have to wait after the i-th day to get a warmer temperature. If there is no future day, keep answer[i] == 0.
temps = [73, 74, 75, 71, 69, 72, 76, 73]
Stack (indices of waiting days):
1. i=0 (73): Push 0. Stack: [0]
2. i=1 (74): 74 > 73. Pop 0, ans[0]=1-0=1. Push 1. Stack: [1]
3. i=2 (75): 75 > 74. Pop 1, ans[1]=2-1=1. Push 2. Stack: [2]
4. i=3 (71): Push 3. Stack: [2, 3]
5. i=4 (69): Push 4. Stack: [2, 3, 4]
6. i=5 (72): 72 > 69. Pop 4, ans[4]=1. 72 > 71. Pop 3, ans[3]=2. Stack: [2, 5]
7. i=6 (76): 76 > 72. Pop 5, ans[5]=1. 76 > 75. Pop 2, ans[2]=4. Stack: [6]Examples
Intuition
For each day, simply look ahead to the right to find the first day with a higher temperature. This is the most natural approach but is slow for large inputs.
Intuition
Use a stack to store indices of temperatures that haven't found a warmer day yet. As we iterate, if the current temperature is warmer than the temperature at the index on top of the stack, we've found our 'warmer day' for that index. This efficiently ensures each element is pushed/popped once.
Detailed Dry Run
| i | Temp | Action | Stack Status | ans[idx] |
|---|---|---|---|---|
| 3 | 71 | Push | [2, 3] | - |
| 4 | 69 | Push | [2, 3, 4] | - |
| 5 | 72 | Pop 4, Pop 3 | [2, 5] | ans[4]=1, ans[3]=2 |
| 6 | 76 | Pop 5, Pop 2 | [6] | ans[5]=1, ans[2]=4 |
⚠️ Common Pitfalls & Tips
Remember that the stack should store indices, not values, so you can calculate the distance between days.
Next Greater Element I
Find the next greater element for each element in nums1 within nums2. The next greater element of x in nums2 is the first element to its right that is larger than x.
nums1 = [4, 1, 2], nums2 = [1, 3, 4, 2]
Processing nums2 to find next greater:
1. 1: Stack [1]
2. 3: 3 > 1. Map[1]=3. Pop, Push 3. Stack [3]
3. 4: 4 > 3. Map[3]=4. Pop, Push 4. Stack [4]
4. 2: 2 < 4. Push 2. Stack [4, 2]
Result Map: {1:3, 3:4}
For nums1: 4 -> -1, 1 -> 3, 2 -> -1
Output: [-1, 3, -1]Examples
Intuition
For each element in nums1, find its position in nums2, then linearly scan rightward to find the first element greater than it. Simple, but performs redundant scans.
Intuition
Instead of linearly searching for each element of nums1 in nums2 every time, we can pre-store the indices of all elements in nums2 in a hash map. This allows us to jump directly to the correct starting point in nums2 for the scan, eliminating the 'find index' part of the brute force.
Intuition
Use a monotonic decreasing stack to find the next greater element for all elements in nums2 in a single pass. Store the results in a hash map for O(1) lookups when processing nums1.
Detailed Dry Run
| Num (nums2) | Stack | Map Update | Action |
|---|---|---|---|
| 1 | [1] | - | Push |
| 3 | [3] | 1 -> 3 | Pop 1, Push 3 |
| 4 | [4] | 3 -> 4 | Pop 3, Push 4 |
| 2 | [4, 2] | - | Push |
⚠️ Common Pitfalls & Tips
Ensure nums1 is actually a subset of nums2 as per problem constraints. The stack approach only works for finding the next greater element.
Next Greater Element II
Given a circular integer array nums, return the next greater number for every element. Circularly means after the last element, we search from the beginning.
nums = [1, 2, 1]
Pass 1:
1. Index 0 (1): Stack [0]
2. Index 1 (2): 2 > 1. ans[0]=2. Pop 0, Push 1. Stack [1]
3. Index 2 (1): 1 < 2. Push 2. Stack [1, 2]
Pass 2 (Circular):
4. i=0 (1): 1 < 1 (top). Skip.
5. i=1 (2): 2 > 1 (top). ans[2]=2. Pop 2. Stack [1]
Final Result: [2, -1, 2]Examples
Intuition
For each element, simulate the circular search by iterating up to 2N positions using modulo. When we find the first element greater than the current one, store the distance. This is straightforward but slower.
Intuition
Instead of two physical passes or flattening the array, we can use the modulo operator % to wrap around. For each element at index i, we check elements from (i+1) % n to (i + n - 1) % n. This is the same complexity as Level I but emphasizes the use of modular arithmetic for circular data structures.
Intuition
To handle the circularity, we can conceptually concatenate the array with itself. In practice, we iterate from 0 to 2N-1 and use modulo i % N to access elements. A monotonic stack stores indices of elements looking for their next greater neighbor.
Detailed Dry Run
| i | i % N | Val | Stack | ans Update |
|---|---|---|---|---|
| 0 | 0 | 1 | [0] | - |
| 1 | 1 | 2 | [1] | ans[0]=2 |
| 2 | 2 | 1 | [1, 2] | - |
| 3 | 0 | 1 | [1, 2] | - |
| 4 | 1 | 2 | [1] | ans[2]=2 |
⚠️ Common Pitfalls & Tips
Modding i % n is essential for circular behavior. Only push to the stack in the first pass i < n to avoid infinite loops or duplicates.
Largest Rectangle in Histogram
Given an array of integers heights representing the histogram's bar height where the width of each bar is 1, find the area of the largest rectangle in the histogram.
heights = [2, 1, 5, 6, 2, 3]
Processing:
1. 2: Stack [2]
2. 1: 1 < 2. Pop 2. Area = 2 * (1) = 2. Push 1.
3. 5: 5 > 1. Push 5.
4. 6: 6 > 5. Push 6.
5. 2: 2 < 6. Pop 6. Area = 6 * (1) = 6.
2 < 5. Pop 5. Area = 5 * (2) = 10.
Push 2.
...
Max Area: 10 (from heights 5 and 6)Examples
Intuition
For every possible pair of bars (i, j), the height of the rectangle is determined by the shortest bar between them. Calculate the area for all such pairs and track the maximum.
Intuition
As we iterate through the heights, we maintain a monotonic increasing stack of indices. If the current bar is shorter than the bar at the stack top, the stack top's rectangle is limited by the current bar. We pop the stack top and calculate its area: the width is the distance between the current index and the new stack top's index.
Detailed Dry Run
| i | h[i] | Stack | Action | Area Calculation |
|---|---|---|---|---|
| 0 | 2 | [0] | Push | - |
| 1 | 1 | [1] | Pop 0 | h[0]=2, w=1-0=1, Area=2 |
| 2 | 5 | [1, 2] | Push | - |
| 3 | 6 | [1, 2, 3] | Push | - |
| 4 | 2 | [1, 4] | Pop 3, 2 | h[3]=6, w=4-2-1=1, A=6; h[2]=5, w=4-1-1=2, A=10 |
Maximal Rectangle
Given a rows x cols binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area.
Matrix:
[ ["1","0","1","0","0"],
["1","0","1","1","1"],
["1","1","1","1","1"],
["1","0","0","1","0"] ]
Row 0: [1, 0, 1, 0, 0] -> Hist Max: 1
Row 1: [2, 0, 2, 1, 1] -> Hist Max: 2
Row 2: [3, 1, 3, 2, 2] -> Hist Max: 6 (The answer)
Row 3: [4, 0, 0, 3, 0] -> Hist Max: 4Examples
Intuition
This problem is a 2D extension of 'Largest Rectangle in Histogram'. We can treat each row as the base of a histogram where the 'height' of each column is the number of consecutive 1's above it. For each row, we maintain these heights and call the histogram function to find the max rectangle at that row.
Trapping Rain Water
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.
heights = [0, 1, 0, 2]
1. Index 0 (0): Push 0.
2. Index 1 (1): 1 > 0. Pop 0. Stack empty. Push 1.
3. Index 2 (0): 0 < 1. Push 2.
4. Index 3 (2): 2 > 0. Pop 2 (bottom). Top is 1 (left wall).
- height = min(2, 1) - 0 = 1
- width = 3 - 1 - 1 = 1
- Water = 1 * 1 = 1Examples
Intuition
Use a stack to keep track of indices of bars in decreasing order of height. When we see a bar taller than the top of the stack, it means the bar at the top can trap water. The water level is limited by the current bar and the bar before the stack top (new top).
Simplify Path
Given a Unix-style absolute path, simplify it to its canonical form.
path = "/home//foo/../bar/"
Tokens: ["home", "", "foo", "..", "bar"]
1. "home": Valid name. Push. Stack: ["home"]
2. "": Empty. Skip.
3. "foo": Valid name. Push. Stack: ["home", "foo"]
4. "..": Parent dir. Pop. Stack: ["home"]
5. "bar": Valid name. Push. Stack: ["home", "bar"]
Canonical Path: "/home/bar"Examples
Intuition
Split the path by '/' into parts. Maintain a list acting as a stack: push valid directory names, pop for '..', skip '.' and empty parts. Join the stack with '/' prefixed by '/'.
Intuition
Instead of using an explicit stack, we can process the path string repeatedly by removing the most 'reducible' parts. For example, replacing /./ with / and /directory_name/../ with /. This is less efficient but helpful for understanding the underlying LIFO structure of the path.
Intuition
Split the path by / and process each component. A stack maintains the valid directory structure. .. removes the last added directory, while . or empty strings are ignored. Any other string is a valid directory to be pushed.
Detailed Dry Run
| Part | Action | Stack Content |
|---|---|---|
| "home" | Push | ["home"] |
| "foo" | Push | ["home", "foo"] |
| ".." | Pop | ["home"] |
| "bar" | Push | ["home", "bar"] |
⚠️ Common Pitfalls & Tips
Multiple slashes become one. .. at the root keeps you at the root. The trailing slash should be removed unless it's the root itself.
Generate Parentheses
The number of valid strings with n pairs of parentheses is the n-th Catalan Number: . This grows roughly as .
A prefix of a parenthesis string is valid if and only if:
) does not exceed the number of opening brackets (.n.2n and the final counts of ( and ) are equal.Instead of generating all sequences and checking them, we only explore branches that satisfy the balance conditions.
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
Examples
Intuition
Generate all possible strings of length 2n consisting of '(' and ')'. Check each string for validity using a stack or a counter.
Use recursion to generate all sequences. For each sequence, verify if the balance ever goes negative and ends at zero.
Detailed Dry Run
n=1 Sequences: "((" (Invalid), "()" (Valid), ")(" (Invalid), "))" (Invalid).
Intuition
Only add a parenthesis if it keeps the sequence valid. We only add '(' if we haven't used n of them, and only add ')' if the number of ')' is less than the number of '('.
Maintain open and close counts. If open < n, we can branch with '('. If close < open, we can branch with ')'.
Detailed Dry Run
n=2
Intuition
Every non-empty valid string S can be uniquely written as (A)B, where A and B are valid (potentially empty) parenthesis strings.
To generate all strings of length 2n, we iterate over all possible lengths of A. For a fixed i, where i is the number of pairs in A, we have n-i-1 pairs in B.
Detailed Dry Run
n=2
Asteroid Collision
Calculate the final state of asteroids after all possible collisions. Positive integers move right, negative move left. If two asteroids meet, the smaller one explodes. If they are the same size, both explode.
asteroids = [5, 10, -5]
1. 5: Push. Stack: [5]
2. 10: Push (same direction). Stack: [5, 10]
3. -5: Moves left. 10 > |-5|. -5 explodes.
Final State: [5, 10]Examples
Intuition
Repeatedly scan the list for the first pair of adjacent asteroids that collide (positive followed immediately by negative). Resolve it and restart from the beginning. Repeat until no more collisions exist.
Intuition
Use a stack to track asteroids moving right. When a left-moving asteroid appears, it collides with right-moving ones on the stack top. Continue popping from the stack as long as the right-moving asteroid is smaller. Handle tie-breaks where both explode.
Detailed Dry Run
| Ast | Action | Stack Status | Collision Result |
|---|---|---|---|
| 5 | Push | [5] | - |
| 10 | Push | [5, 10] | - |
| -5 | Compare | [5, 10] | -5 explodes (10 > 5) |
| -12 | Compare | [] | 10 explodes, 5 explodes, -12 stays |
⚠️ Common Pitfalls & Tips
Be careful handle the case where a left-moving asteroid destroys everything in the stack and still survives. Also, ensure tie-cases (same size) are handled correctly.
Remove All Adjacent Duplicates in String II
Repeatedly remove k adjacent and equal characters from string s until no more such removals are possible.
s = "deeedbbcccbdaa", k = 3
1. Process 'd': Stack [(d, 1)]
2. Process 'e': Stack [(d, 1), (e, 1)]
3. Process 'e': Stack [(d, 1), (e, 2)]
4. Process 'e': Stack [(d, 1), (e, 3)] -> Count=3! Pop (e).
5. Process 'd': Stack [(d, 2)]
...
Result: "aa"Examples
Intuition
Repeatedly scan the string, looking for any group of k consecutive identical characters. When found, remove them and restart the scan. Repeat until no more groups of k are found. This is slow but conceptually simple.
Intuition
Use a stack to keep track of characters and their contiguous counts. When the next character matches the top of the stack, increment the top's count. If it matches k, pop it. If it doesn't match, push a new pair (char, 1).
Detailed Dry Run
| Char | Action | Stack (Top Pair) | Length |
|---|---|---|---|
| 'd' | Push | (d, 1) | 1 |
| 'e' | Push | (e, 1) | 2 |
| 'e' | Incr | (e, 2) | 2 |
| 'e' | Pop | - | 1 |
⚠️ Common Pitfalls & Tips
Ensure the stack stores pairs so you don't have to pop characters one by one. Rebuilding the string efficiently at the end is also key.
Online Stock Span
Calculate the 'span' of a stock's price today: the number of consecutive days (including today) the price was less than or equal to today's price.
Prices: [100, 80, 60, 70, 60, 75, 85]
1. 100: Push (100, 1). Stack: [(100, 1)]
2. 80: Push (80, 1). Stack: [(100, 1), (80, 1)]
3. 70: Pop 60(1). Push (70, 2). Stack: [(100, 1), (80, 1), (70, 2)]
4. 75: Pop 60(1), 70(2). Push (75, 4). Stack: [(100, 1), (80, 1), (75, 4)]Examples
Intuition
For each new price, scan backwards through all previous prices and count consecutive days where price <= today's price. Stop as soon as a higher price is found.
Intuition
Use a stack to store pairs of (price, span). When a new price is added, pop all elements with price less than or equal to the current one, adding their spans to the current span before pushing it onto the stack.
Detailed Dry Run
| price | Action | Stack Status | Span Output |
|---|---|---|---|
| 100 | Push | [(100, 1)] | 1 |
| 80 | Push | [(100, 1), (80, 1)] | 1 |
| 70 | Pop 60(1) | [(100, 1), (80, 1), (70, 2)] | 2 |
| 85 | Pop 75, 80 | [(100, 1), (85, 6)] | 6 |
⚠️ Common Pitfalls & Tips
Be sure to store the accumulated span span += stack.pop()[1] so that the current element captures all smaller previous spans in constant time.
Validate Stack Sequences
Determine if a sequence of indices popped could have resulted from a sequence of pushed operations on an empty stack.
pushed = [1, 2, 3], popped = [2, 3, 1]
1. Push 1: Stack [1]
2. Push 2: Stack [1, 2]. Top 2 == popped[0]. Pop. Stack [1]
3. Push 3: Stack [1, 3]. Top 3 == popped[1]. Pop. Stack [1]
4. No more pushes. Top 1 == popped[2]. Pop. Stack []
Result: TrueExamples
Intuition
Without insight, we could try all possible sequences of push/pop operations on pushed[] to check if any of them matches popped[]. However, given that each element must be pushed exactly once, the simulation approach is actually already optimal in practice. The 'brute force' here is to consider a naive simulation without the greedy insight.
Intuition
Iterate through the pushed array and push each element onto a stack. After each push, greedily pop elements from the stack as long as the stack top matches the current element in the popped array.
Detailed Dry Run
| Push | Stack | Popped Pointer | Action |
|---|---|---|---|
| 1 | [1] | 0 | Push |
| 2 | [1, 2] | 0 | Push |
| - | [1] | 1 (2 matched) | Pop |
| 3 | [1, 3] | 1 | Push |
| - | [] | 3 (3 then 1 matched) | Pop, Pop |
⚠️ Common Pitfalls & Tips
All elements in pushed and popped are distinct as per standard constraints. Ensure you pop as many matching elements as possible after each push.
Basic Calculator II
Implement a basic calculator to evaluate a simple expression string containing non-negative integers, +, -, *, / operators and empty spaces.
s = "3+2*2"
1. '3': num = 3
2. '+': Push 3. num = 0, sign = '+'
3. '2': num = 2
4. '*': 2*2 = 4 (High precedence).
5. Result: 3 + 4 = 7Examples
Intuition
Without a stack, we can handle operator precedence in two passes: First, scan for * and / and evaluate them in-place (replacing the two operands and the operator with the result). Then, sum all remaining numbers separated by + and -. This avoids the stack but requires two traversals.
Intuition
Use a stack to store values. For + and -, push the number (negatively for -). For * and /, pop the last value, perform the operation with the current number, and push it back. Finally, sum the stack.
Detailed Dry Run
| Char | Num | Sign | Stack | Action |
|---|---|---|---|---|
| '3' | 3 | '+' | [] | - |
| '+' | 0 | '+' | [3] | Push 3 |
| '2' | 2 | '+' | [3] | - |
| '*' | 0 | '*' | [3] | - |
| '2' | 2 | '*' | [3] | - |
| End | - | - | [3, 4] | Pop 2, Push 2*2 |
⚠️ Common Pitfalls & Tips
Division should truncate toward zero. Be careful with spaces and multi-digit numbers. Handle the last number in the string properly.
Decode String
Decode an encoded string where k[encoded_string] means encoded_string is repeated k times.
s = "3[a]2[bc]"
1. '3': k = 3
2. '[': Push 3, Push "". Stack: [(3, "")]
3. 'a': curr = "a"
4. ']': Pop 3. res = "" + "a"*3 = "aaa"
5. '2': k = 2
6. '[': Push 2, Push "aaa". Stack: [(2, "aaa")]
7. "bc": curr = "bc"
8. ']': Pop 2. res = "aaa" + "bc"*2 = "aaabcbc"Examples
Intuition
Find the innermost [] bracket pair using regex or string searching, expand it (repeat the inner string by the preceding number), replace it in the original string, and repeat until no brackets remain. This naturally handles nested brackets by working from inside out.
Intuition
Use two stacks: one to store the multipliers (countStack) and one to store the strings built so far (resStack). When an opening bracket [ is met, push the current count and current string. When a closing bracket ] is met, pop the multiplier and the previous string, then append the repeated current string to the previous one.
Detailed Dry Run
| Char | countStack | resStack | currStr |
|---|---|---|---|
| '3' | [] | [] | "" (k=3) |
| '[' | [3] | [""] | "" |
| 'a' | [3] | [""] | "a" |
| ']' | [] | [] | "aaa" |
⚠️ Common Pitfalls & Tips
Multi-digit numbers (like 100[a]) must be handled. Nested brackets are naturally handled by the stack.
Remove Duplicate Letters
Remove duplicate letters from string s so that every letter appears once. The result must be the smallest in lexicographical order among all possible results.
s = "bcabc"
Counts: {b:2, c:2, a:1}
1. 'b': Push. Stack: [b]. Counts: {b:1, c:2, a:1}
2. 'c': Push. Stack: [b, c]. Counts: {b:1, c:1, a:1}
3. 'a':
- 'c' > 'a' and c's count > 0. Pop c.
- 'b' > 'a' and b's count > 0. Pop b.
- Push 'a'. Stack: [a]. Counts: {b:1, c:1, a:0}
4. 'b': Push. Stack: [a, b]. Counts: {b:0, c:1, a:0}
5. 'c': Push. Stack: [a, b, c]. Counts: {b:0, c:0, a:0}
Result: "abc"Examples
Intuition
Generate all unique subsequences of the string that contain each character exactly once (using a set of frequency tracking). Filter to keep valid ones and return the lexicographically smallest. This is exponential and only feasible for tiny inputs.
Intuition
Maintain a stack of characters that represents the current smallest lexicographical result. Use a frequency map to know if a character will appear later. If the current character is smaller than the stack top and the stack top appears again later, pop the stack top. Use a seen set to skip characters already in the result.
Detailed Dry Run
| Char | Action | Stack | Seen | Count Left |
|---|---|---|---|---|
| 'b' | Push | [b] | {b} | b:1 |
| 'c' | Push | [b, c] | {b, c} | c:1 |
| 'a' | Pop b, c; Push a | [a] | {a} | a:0 |
| 'b' | Push | [a, b] | {a, b} | b:0 |
⚠️ Common Pitfalls & Tips
You can only pop a character if you know it appears later in the string. Character counts or last-occurrence indices are necessary.
Basic Calculator
Implement a basic calculator to evaluate a simple expression string containing (, ), +, -, non-negative integers and empty spaces.
s = " 1 + (4 + 5 - 2) - 3 "
1. '1': res = 1, sign = +
2. '+': Push current res (1) and sign (+) to stack.
3. '(': Start new group.
4. Groups: (4 + 5 - 2) = 7
5. Pop: 1 + (+)7 = 8
6. '-': res = 8, sign = -
7. '3': res = 8 - 3 = 5Examples
Intuition
Process the string character by character. Use a stack to handle parentheses by saving the current result and sign before entering a new group. For + and -, apply them to the running total. For (, push the state; for ), pop and combine.
Detailed Dry Run
| Char | Running Total | Sign | Stack |
|---|---|---|---|
| '1' | 1 | 1 | [] |
| '+' | 1 | 1 | [] |
| '(' | 0 | 1 | [(1, 1)] |
| '4' | 4 | 1 | [(1, 1)] |
⚠️ Common Pitfalls & Tips
Ensure the last number is added. Be careful with large results (use long long in C++). Handle nested parentheses correctly by pushing both result and sign.
Basic Calculator III
Evaluate an expression string containing (, ), +, -, *, /, non-negative integers and spaces. This is the ultimate calculator problem.
s = "2*(5+5*2)/3+(6/2+8)"
1. Handle parentheses recursively or using a stack stack.
2. Within each group, apply multiply/divide first, then add/subtract.
3. Result: 2*(15)/3 + (11) = 10 + 11 = 21Examples
Intuition
This problem combines Basic Calculator I and II. The most natural solution uses recursion to handle parentheses: every time we see (, we recurse; every time we see ), we return the result of the current sub-expression. Within each level, we use the standard 'accumulator' logic for +, -, *, /.
⚠️ Common Pitfalls & Tips
Sentinel + at the end makes the loop logic cleaner for the last number. Be careful with division truncation in Python.
Expression Add Operators
Adding + and - is straightforward: just add or subtract the current value. However, multiplication (*) has higher precedence. If we have 1 + 2 * 3, we cannot simply compute (1 + 2) * 3.
To handle prev + current * next, we need to subtract current and add current * next.
Formula: NewTotal = (OldTotal - prevOperand) + (prevOperand * currentValue).
"05" is invalid unless it's just "0".long (64-bit integers).Given a string num containing only digits and an integer target, return all possibilities to insert the binary operators +, -, and/or * between the digits of num so that the resultant expression evaluates to the target value.
Note that operands in the returned expressions should not contain leading zeros.
Examples
Intuition
Insert every possible operator between digits to generate all possible expressions, then evaluate each one.
Generate expressions by recursion. For each string of length , there are combinations. This is very slow due to the evaluation step.
Detailed Dry Run
num="12", target=3.
Intuition
Calculate the expression value on the fly to avoid explicit evaluation. Handle multiplication by tracking the previous added value.
Use dfs(index, path, currentVal, prevOperand). If we add *, the new value is (currentVal - prevOperand) + (prevOperand * now).
Detailed Dry Run
num="123", target=6
Intuition
Instead of building string paths and then converting them, use a char[] or byte[] to build the path directly, avoiding string concatenations which are expensive.
Pass a char[] (or byte[] in Go) to the recursive function. When adding an operator or digit, update the array and its current length. Backtrack by resetting the length.
Detailed Dry Run
num="123", target=6
Help us improve! Report bugs or suggest new features on our Telegram group.