Builds on Next Greater Element, which you haven’t finished yet.
Monotonic Stack (Trapping Rain Water)
You're given n non-negative integers height describing an elevation map, where every bar is width 1. Work out the total volume of water this terrain would trap after rain.
Valley Water
bar heights ➔ stack pops each valley floor ➔ width * (lower wall - floor) per valley| 1 | def trap(height: list[int]) -> int: |
| 2 | """Trapping Rain Water: a monotonic stack closes each valley when its right wall arrives. |
| 3 | Time: O(N) | Space: O(N) |
| 4 | """ |
| 5 | stack: list[int] = [] # Indices whose heights never increase from bottom to top: valley floors and left walls |
| 6 | total = 0 |
| 7 | |
| 8 | for i, h in enumerate(height): |
| 9 | # 1. Close Every Valley That h Is The Right Wall Of: |
| 10 | # While h is taller than the bar on top, that bar is a valley floor and h is its right wall: pop it. |
| 11 | while stack and height[stack[-1]] < h: |
| 12 | valley_i = stack.pop() |
| 13 | |
| 14 | # 2. Stop When The Floor Has No Left Wall: |
| 15 | # An empty stack after the pop means nothing taller stands to the left, so this floor holds no water. |
| 16 | if not stack: |
| 17 | break |
| 18 | |
| 19 | # 3. Add The Water Above The Floor, Between The Two Walls: |
| 20 | # The level is the lower wall, and the floor is not a wall, so subtract it. |
| 21 | left_i = stack[-1] |
| 22 | width = i - left_i - 1 |
| 23 | total += width * (min(height[left_i], h) - height[valley_i]) |
| 24 | |
| 25 | stack.append(i) # i survived every pop, so it can still be a wall or a floor later |
| 26 | |
| 27 | return total |
stack never increase from bottom to top, so the bar under a popped floor is the nearest bar to its left that is at least as tall.Keep stack as bar indices whose heights never increase from bottom to top. When a taller bar h arrives at i, it closes the valley under it: pop the floor valley_i, and the bar now on top is the left wall left_i. The water above the floor sits between the two walls, so its width is i - left_i - 1 and its depth is the lower wall minus the floor, min(height[left_i], h) - height[valley_i]. The trap is the empty stack after the pop: the floor then has no left wall and holds no water, so break before reading stack[-1].
See it run
Monotonic stack for Trapping Rain Water, step by step
stack []total 0
The top row is height. The column number is the index. The bottom row is the stack: the left cell is the bottom, each cell holds an index, and a blank cell is an unused slot.
hthe bar being read now; during a pop it is the right wallfloorvalley_i, the bar just poppedleft wallleft_i, the bar now on top of the stackpushedthe index that was just pushed
Start: stack is empty and total = 0.
Gotcha 1 of 3
- The trap
Gotcha 1: Reading
stack[-1]after a pop that emptied the stackMistakepassFixif not stack: breakWhat happens: When the popped floor was the only bar on
stack, no bar stands to its left, so there is no left wall. Readingstack[-1]on an empty list raises an error. Thebreakalso stops the loop from adding water that nothing holds.Example: height = [1, 2]Mistake: raises IndexErrorFix: returns 0
- In Largest Rectangle in Histogram (LC 84) the popped bar is the height of a rectangle. Here the popped bar is only the floor, and the two walls set the level of the water. See Histogram Stack
Deep Dive
What a pop adds: it closes one valley
This card uses the same stack as Monotonic Stack, Next Greater Element. The heights at the indices in stack never increase from bottom to top. A taller bar pops the shorter bars above it. In Next Greater Element the pop writes a distance. Here the pop closes a valley and adds water.
When bar h at index i pops index valley_i, three bars are known. The popped bar is the floor. The bar now on top, left_i, is the nearest bar to the left that is at least as tall as the floor. The arriving bar h is the first bar to the right that is taller than the floor.
The water above the floor sits between the two walls. Its width is i - left_i - 1, because the walls themselves hold no water. Its depth is min(height[left_i], h) - height[valley_i]. The floor is subtracted because the water starts above it.
Example: height = [3, 0, 2]. Index 2 has h = 2. It pops index 1 as the floor. left_i = 0, so width = 2 - 0 - 1 = 1, and the depth is min(3, 2) - 0 = 2. The total is 2.
One trace: layers of water, and the empty stack
The board replays height = [4, 2, 0, 3, 2, 5]. The top row is height. The bottom row is stack, and each cell holds an index.
Each pop adds one horizontal layer of water. Index 3 (height 3) pops index 2 as the floor and adds 1 * (2 - 0) = 2. It then pops index 1 and adds 2 * (min(4, 3) - 2) = 2. Now stack is [0, 3], and total is 4.
Index 5 (height 5) pops index 4 and adds 1 * (3 - 2) = 1. It pops index 3 and adds 4 * (4 - 3) = 4. Then it pops index 0. The stack is empty, so the floor has no left wall. This is the step marked as the trap on the board.
The break leaves the loop before stack[-1] is read. Without it, stack[-1] on an empty list raises an error. The layers add up to 2 + 2 + 1 + 4 = 9, which is the answer.
Why the layers add up to the right total
The water above index k is min(max_left, max_right) - height[k], where max_left and max_right are the tallest bars on each side. The stack computes the same amount in horizontal layers instead of one column at a time.
Each water cell sits above exactly one floor bar and between exactly one pair of walls: the nearest taller bar on each side. The pop that removes that floor adds that cell once. No cell is added twice, because a bar is popped once.
A bar of equal height needs no special case. For height = [2, 0, 2], index 2 pops index 1 and adds 1 * (min(2, 2) - 0) = 2. Index 0 has height 2, which is not below 2, so it stays on stack.
If a floor is popped and stack is empty, nothing on the left can hold the water, so the layer is empty. For height = [1, 2] the function returns 0.
The cost, and stack or two pointers
Every index is pushed once and popped at most once, so the time is O(N). stack can hold all N indices when the heights fall from left to right, so the space is O(N).
Example: for 1000 bars, the loop does at most 1000 pushes and 1000 pops.
The stack adds water one horizontal layer at a time. A pop closes one valley, and the layer is width * depth.
Trapping Rain Water (LC 42) also has a two-pointer solution with O(N) time and O(1) space. It adds water one column at a time. The water above index k is min(max_left, max_right) - height[k].
Two pointers work because the water level above a bar depends on the tallest bar on each side. If max_left < max_right, the left side sets the level, whatever lies between. So the pointer on the shorter side can move in safely.
Largest Rectangle in Histogram (LC 84) has no such rule. Its walls are the nearest lower bars, and not the tallest bars. That is why it needs the stack, and why this card is the same pattern with the stack order reversed.
Interview tip: give the two-pointer solution as the optimal one, because it uses O(1) space. Then explain the stack solution to show that you can resolve the left wall and the right wall of each valley.
Container With Most Water (LC 11) is a different problem. It picks two bars and measures the area between them, and it does not need a stack.
60-Second Pitch
"I keep a stack of indices whose heights never increase. When a taller bar arrives at i, it closes the valley under it. I pop the floor valley_i, and the new top is the left wall left_i. The water above the floor has width i - left_i - 1 and depth min(height[left_i], h) - height[valley_i]. If the stack is empty after the pop, there is no left wall, so I break. Each index is pushed and popped at most once. The time is O(N) and the space is O(N)."
Common Questions
- How is this different from the Next Greater card?
The stack and the pops are the same. In Next Greater the pop writes a distance. Here the pop closes a valley: the popped bar is the floor, the new top is the left wall, and the arriving bar is the right wall.
- Can I use the same two-pointer method for Largest Rectangle in Histogram (LC 84)?
No. Here the water level depends on the tallest bar on each side, so the shorter side tells you which pointer is safe to move. In Largest Rectangle in Histogram (LC 84) each bar is limited by the nearest lower bar on each side, and two pointers at the ends cannot find those bars. Use the stack there.
- Why do I need
if not stack: break? After the pop,
stack[-1]is the left wall. If the popped floor was the only index,stackis empty and there is no left wall. Readingstack[-1]would raise an error. The floor holds no water, so the loop stops.- Why is the depth
min(height[left_i], h) - height[valley_i]? The water level is the lower of the two walls. The floor is not part of the water, so its height is subtracted. For
[3, 0, 2], the level ismin(3, 2) = 2, and the depth is 2.- Why does the total not count a cell twice?
Each water cell has one floor bar below it and one pair of nearest taller walls. The pop of that floor adds the cell. A bar is popped once, so the cell is added once.
SWE Reasoning
Spot it
- The question asks how much water, paint or volume a row of bars can hold between taller bars.
- Each dip is closed by the first taller bar on its right and by the nearest tall bar on its left.
- You know the next-greater stack and need one more value for each pop: the left wall.
- The answer is a sum over valleys, and the width of each valley depends on two indices.
- An O(N^2) search that scans left and right from every bar is too slow.
The invariant, step by step
stackstarts empty. It will hold indices whose heights never increase from bottom to top.- Bar
harrives at indexi. While the top bar is lower thanh, that top is a floor andhis its right wall. - Pop the floor as
valley_i. Ifstackis empty, the floor has no left wall, sobreak. - Otherwise
left_i = stack[-1]is the left wall, andwidth = i - left_i - 1. - Add
width * (min(height[left_i], h) - height[valley_i])tototal. - When the loop stops, push
i. The heights onstackstill do not increase from bottom to top.
Traps
- Reading
stack[-1]after a pop that emptiedstack. There is no left wall, so the read raises an error. Useif not stack: break. - Forgetting to subtract the floor. Use
min(height[left_i], h) - height[valley_i]. The wrong line gives 27 and not 9 for[4, 2, 0, 3, 2, 5]. width = i - left_i. The wall bar holds no water, so the width isi - left_i - 1.- Using the wrong wall height. The water level is the lower wall, so use
min(height[left_i], h). - Pushing the height and not the index. The width needs the index.
Practice
Builds on Next Greater Element, which you haven’t finished yet.
Monotonic Stack (Trapping Rain Water)
You're given n non-negative integers height describing an elevation map, where every bar is width 1. Work out the total volume of water this terrain would trap after rain.
Valley Water
bar heights ➔ stack pops each valley floor ➔ width * (lower wall - floor) per valley| 1 | def trap(height: list[int]) -> int: |
| 2 | """Trapping Rain Water: a monotonic stack closes each valley when its right wall arrives. |
| 3 | Time: O(N) | Space: O(N) |
| 4 | """ |
| 5 | stack: list[int] = [] # Indices whose heights never increase from bottom to top: valley floors and left walls |
| 6 | total = 0 |
| 7 | |
| 8 | for i, h in enumerate(height): |
| 9 | # 1. Close Every Valley That h Is The Right Wall Of: |
| 10 | # While h is taller than the bar on top, that bar is a valley floor and h is its right wall: pop it. |
| 11 | while stack and height[stack[-1]] < h: |
| 12 | valley_i = stack.pop() |
| 13 | |
| 14 | # 2. Stop When The Floor Has No Left Wall: |
| 15 | # An empty stack after the pop means nothing taller stands to the left, so this floor holds no water. |
| 16 | if not stack: |
| 17 | break |
| 18 | |
| 19 | # 3. Add The Water Above The Floor, Between The Two Walls: |
| 20 | # The level is the lower wall, and the floor is not a wall, so subtract it. |
| 21 | left_i = stack[-1] |
| 22 | width = i - left_i - 1 |
| 23 | total += width * (min(height[left_i], h) - height[valley_i]) |
| 24 | |
| 25 | stack.append(i) # i survived every pop, so it can still be a wall or a floor later |
| 26 | |
| 27 | return total |
stack never increase from bottom to top, so the bar under a popped floor is the nearest bar to its left that is at least as tall.Keep stack as bar indices whose heights never increase from bottom to top. When a taller bar h arrives at i, it closes the valley under it: pop the floor valley_i, and the bar now on top is the left wall left_i. The water above the floor sits between the two walls, so its width is i - left_i - 1 and its depth is the lower wall minus the floor, min(height[left_i], h) - height[valley_i]. The trap is the empty stack after the pop: the floor then has no left wall and holds no water, so break before reading stack[-1].
See it run
Monotonic stack for Trapping Rain Water, step by step
stack []total 0
The top row is height. The column number is the index. The bottom row is the stack: the left cell is the bottom, each cell holds an index, and a blank cell is an unused slot.
hthe bar being read now; during a pop it is the right wallfloorvalley_i, the bar just poppedleft wallleft_i, the bar now on top of the stackpushedthe index that was just pushed
Start: stack is empty and total = 0.
Gotcha 1 of 3
- The trap
Gotcha 1: Reading
stack[-1]after a pop that emptied the stackMistakepassFixif not stack: breakWhat happens: When the popped floor was the only bar on
stack, no bar stands to its left, so there is no left wall. Readingstack[-1]on an empty list raises an error. Thebreakalso stops the loop from adding water that nothing holds.Example: height = [1, 2]Mistake: raises IndexErrorFix: returns 0
- In Largest Rectangle in Histogram (LC 84) the popped bar is the height of a rectangle. Here the popped bar is only the floor, and the two walls set the level of the water. See Histogram Stack
Deep Dive
What a pop adds: it closes one valley
This card uses the same stack as Monotonic Stack, Next Greater Element. The heights at the indices in stack never increase from bottom to top. A taller bar pops the shorter bars above it. In Next Greater Element the pop writes a distance. Here the pop closes a valley and adds water.
When bar h at index i pops index valley_i, three bars are known. The popped bar is the floor. The bar now on top, left_i, is the nearest bar to the left that is at least as tall as the floor. The arriving bar h is the first bar to the right that is taller than the floor.
The water above the floor sits between the two walls. Its width is i - left_i - 1, because the walls themselves hold no water. Its depth is min(height[left_i], h) - height[valley_i]. The floor is subtracted because the water starts above it.
Example: height = [3, 0, 2]. Index 2 has h = 2. It pops index 1 as the floor. left_i = 0, so width = 2 - 0 - 1 = 1, and the depth is min(3, 2) - 0 = 2. The total is 2.
One trace: layers of water, and the empty stack
The board replays height = [4, 2, 0, 3, 2, 5]. The top row is height. The bottom row is stack, and each cell holds an index.
Each pop adds one horizontal layer of water. Index 3 (height 3) pops index 2 as the floor and adds 1 * (2 - 0) = 2. It then pops index 1 and adds 2 * (min(4, 3) - 2) = 2. Now stack is [0, 3], and total is 4.
Index 5 (height 5) pops index 4 and adds 1 * (3 - 2) = 1. It pops index 3 and adds 4 * (4 - 3) = 4. Then it pops index 0. The stack is empty, so the floor has no left wall. This is the step marked as the trap on the board.
The break leaves the loop before stack[-1] is read. Without it, stack[-1] on an empty list raises an error. The layers add up to 2 + 2 + 1 + 4 = 9, which is the answer.
Why the layers add up to the right total
The water above index k is min(max_left, max_right) - height[k], where max_left and max_right are the tallest bars on each side. The stack computes the same amount in horizontal layers instead of one column at a time.
Each water cell sits above exactly one floor bar and between exactly one pair of walls: the nearest taller bar on each side. The pop that removes that floor adds that cell once. No cell is added twice, because a bar is popped once.
A bar of equal height needs no special case. For height = [2, 0, 2], index 2 pops index 1 and adds 1 * (min(2, 2) - 0) = 2. Index 0 has height 2, which is not below 2, so it stays on stack.
If a floor is popped and stack is empty, nothing on the left can hold the water, so the layer is empty. For height = [1, 2] the function returns 0.
The cost, and stack or two pointers
Every index is pushed once and popped at most once, so the time is O(N). stack can hold all N indices when the heights fall from left to right, so the space is O(N).
Example: for 1000 bars, the loop does at most 1000 pushes and 1000 pops.
The stack adds water one horizontal layer at a time. A pop closes one valley, and the layer is width * depth.
Trapping Rain Water (LC 42) also has a two-pointer solution with O(N) time and O(1) space. It adds water one column at a time. The water above index k is min(max_left, max_right) - height[k].
Two pointers work because the water level above a bar depends on the tallest bar on each side. If max_left < max_right, the left side sets the level, whatever lies between. So the pointer on the shorter side can move in safely.
Largest Rectangle in Histogram (LC 84) has no such rule. Its walls are the nearest lower bars, and not the tallest bars. That is why it needs the stack, and why this card is the same pattern with the stack order reversed.
Interview tip: give the two-pointer solution as the optimal one, because it uses O(1) space. Then explain the stack solution to show that you can resolve the left wall and the right wall of each valley.
Container With Most Water (LC 11) is a different problem. It picks two bars and measures the area between them, and it does not need a stack.
60-Second Pitch
"I keep a stack of indices whose heights never increase. When a taller bar arrives at i, it closes the valley under it. I pop the floor valley_i, and the new top is the left wall left_i. The water above the floor has width i - left_i - 1 and depth min(height[left_i], h) - height[valley_i]. If the stack is empty after the pop, there is no left wall, so I break. Each index is pushed and popped at most once. The time is O(N) and the space is O(N)."
Common Questions
- How is this different from the Next Greater card?
The stack and the pops are the same. In Next Greater the pop writes a distance. Here the pop closes a valley: the popped bar is the floor, the new top is the left wall, and the arriving bar is the right wall.
- Can I use the same two-pointer method for Largest Rectangle in Histogram (LC 84)?
No. Here the water level depends on the tallest bar on each side, so the shorter side tells you which pointer is safe to move. In Largest Rectangle in Histogram (LC 84) each bar is limited by the nearest lower bar on each side, and two pointers at the ends cannot find those bars. Use the stack there.
- Why do I need
if not stack: break? After the pop,
stack[-1]is the left wall. If the popped floor was the only index,stackis empty and there is no left wall. Readingstack[-1]would raise an error. The floor holds no water, so the loop stops.- Why is the depth
min(height[left_i], h) - height[valley_i]? The water level is the lower of the two walls. The floor is not part of the water, so its height is subtracted. For
[3, 0, 2], the level ismin(3, 2) = 2, and the depth is 2.- Why does the total not count a cell twice?
Each water cell has one floor bar below it and one pair of nearest taller walls. The pop of that floor adds the cell. A bar is popped once, so the cell is added once.
SWE Reasoning
Spot it
- The question asks how much water, paint or volume a row of bars can hold between taller bars.
- Each dip is closed by the first taller bar on its right and by the nearest tall bar on its left.
- You know the next-greater stack and need one more value for each pop: the left wall.
- The answer is a sum over valleys, and the width of each valley depends on two indices.
- An O(N^2) search that scans left and right from every bar is too slow.
The invariant, step by step
stackstarts empty. It will hold indices whose heights never increase from bottom to top.- Bar
harrives at indexi. While the top bar is lower thanh, that top is a floor andhis its right wall. - Pop the floor as
valley_i. Ifstackis empty, the floor has no left wall, sobreak. - Otherwise
left_i = stack[-1]is the left wall, andwidth = i - left_i - 1. - Add
width * (min(height[left_i], h) - height[valley_i])tototal. - When the loop stops, push
i. The heights onstackstill do not increase from bottom to top.
Traps
- Reading
stack[-1]after a pop that emptiedstack. There is no left wall, so the read raises an error. Useif not stack: break. - Forgetting to subtract the floor. Use
min(height[left_i], h) - height[valley_i]. The wrong line gives 27 and not 9 for[4, 2, 0, 3, 2, 5]. width = i - left_i. The wall bar holds no water, so the width isi - left_i - 1.- Using the wrong wall height. The water level is the lower wall, so use
min(height[left_i], h). - Pushing the height and not the index. The width needs the index.