This One Pattern Can Help You Reduce Time Complexity from O(n²) to O(n) - Learning Monotonic Stacks

Originally published on Substack .
If you’ve been grinding LeetCode, you've probably stumbled upon this problem:
LEETCODE - 739
”Given an array of daily temperatures, return an array where each element tells you how many days you have to wait until a warmer temperature. If there's no warmer day coming, just put 0.”
Example 1:
Input: temperatures = [73,74,75,71,69,72,76,73]
Output: [1,1,4,2,1,1,0,0]
My brain immediately goes: "Easy! Just check each day against every future day until I find a warmer one."
Spoiler Alert : I failed!!!
The Brute Force (aka first attempt)
Like every confident (read: naive) developer, I dove straight into the nested loop approach:
java
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int[] answer = new int[temperatures.length];
// For each day...
for (int i = 0; i < temperatures.length; i++) {
// Check every future day until we find a warmer one
for (int j = i + 1; j < temperatures.length; j++) {
if (temperatures[j] > temperatures[i]) {
answer[i] = j - i; // Found it! Record the wait time
break; // Stop looking
}
}
// If we never found a warmer day, answer[i] stays 0
}
return answer;
}
}
My thought process:
"Okay, for day 0, I'll scan days 1, 2, 3... until I find something warmer"
"Then for day 1, I'll scan days 2, 3, 4... until I find something warmer"
"This is basically asking 'what's the next greater element?' for each position"
Here, we initialize a result
array of zeros, then for each index i
, we check each future day j
. If we find temperatures[j] > temperatures[i]
, we record j - i
and stop the inner loop. This logic is straightforward and guaranteed to find the answer.
But as we glance at the loop counts, alarm bells ring. In the worst case (e.g., a strictly decreasing sequence of temperatures), the inner loop runs nearly N times for each of the N days, leading to O(N²) comparisons. In short, brute force works, but it’s painfully slow for larger inputs.
To summarize the brute-force approach step-by-step:
Initialize
result
array (all zeros).For each day
i = 0 to n-1
:Look at each following day
j = i+1 ... n-1
.If
temperatures[j] > temperatures[i]
, setresult[i] = j - i
and break (stop searching further).If you finish the loop without finding a warmer day,
result[i]
remains 0⏱️ Time Complexity: O(n²) - In the worst case (like temperatures going [100, 99, 98, 97...]), I'm checking almost every pair of days
💾 Space Complexity: O(1) - At least I'm not using extra space... small victory?
💥 Performance: Works fine for small inputs, but feed it 10⁵ temperatures and it crumbles.
I got slapped by a Time Limit Exceeded Error :(.
After getting a Time Limit Exceeded error, I started thinking differently. The problem wasn't just about finding the next warmer day - it was about finding it efficiently.
That's when I stumbled upon something called a Monotonic Stack in the discussions.
🎯 What The Heck Is A Monotonic Stack?
Imagine you're organizing a stack of books by height. A monotonic stack is just a stack where we maintain a specific order - either increasing or decreasing.
When the elements in a stack are maintained in a increasing order , it’s called Monotonically Increasing Stack, and if maintained in decreasing order, it’s called Monotonically Decreasing Stack.
For our temperature problem, we use a decreasing monotonic stack of indices. This means:
The temperatures at those indices decrease as you go from bottom to top of the stack
We store indices (not the actual temperatures) because we need to calculate the distance between days
Quick CheatSheet: (When to use What)
📉 Decreasing Stack → Find Next/Previous GREATER
Daily Temperatures (next warmer day)
Next Greater Element (next bigger number)
Stock Span (previous higher price)
📈 Increasing Stack → Find Next/Previous SMALLER
Largest Rectangle in Histogram (next shorter bar)
Trapping Rain Water (next lower height)
🔍 Quick Decision:
Ask: "Am I looking for something bigger or smaller?"
Bigger → Use Decreasing Stack
Smaller → Use Increasing Stack
💡 Memory Trick:
Decreasing stack finds Greater elements (opposite directions)
Increasing stack finds Smaller elements (opposite directions)
Why does this work?
Think of it like this: we're keeping a "waiting list" of days that haven't found their warmer day yet. When a really hot day comes along, it can "resolve" multiple waiting days at once!
Here's the key insight that made it click for me:
If today is warmer than yesterday, then today is the answer for yesterday
If today is warmer than the day before yesterday too, then today is also the answer for that day
We can resolve multiple "waiting" days in one go!
So, how does this help? Instead of scanning ahead for each day, we process each day once and use the stack to remember which days are still waiting for a warmer day. Here’s the key idea: when a new day is warmer than the day at the top of the stack, we have found the “next warmer day” for that top element. We can then pop it off and record the wait time. Repeat until the stack’s top is warmer than the current day, then push the current day onto the stack.
In more concrete terms:
Create an empty stack to hold indices of days.
Loop
i
from 0 ton-1
over the temperatures:While the stack is not empty and
temperatures[i] > temperatures[stack.peek()]
, pop the top indexprev = stack.pop()
. That means dayi
is warmer than dayprev
, so setresult[prev] = i - prev
.After popping, continue the while-loop because the current day might warm up multiple earlier days.
Push the current index
i
onto the stack. It now waits for a warmer future day.
Any indices left in the stack by the end never found a warmer day, so their
result[]
stays 0 (we initialized it that way).
This clever trick ensures each index goes onto the stack exactly once and is popped at most once. The while-loop doesn’t do quadratic work; it just processes each element a constant number of times, so the overall time becomes O(N). In other words, the monotonic stack approach “operates in linear time” by resolving many comparisons in bulk.
A mental picture: scan each day one by one. For each day, toss it onto a stack of days that haven’t found warmer days yet. If a day comes along that’s warmer than some days in the stack, those earlier days have their answer right now — they get popped and their wait times set. It’s like a time machine that looks forward from each day and immediately goes back to fill in answers for everyone it passed along the way. Each day gets checked only a few times, making the solution hum along efficiently.
The Monotonic Stack Solution
java
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int[] answer = new int[temperatures.length];
Stack<Integer> stack = new Stack<>(); // Stack of indices
for (int i = 0; i < temperatures.length; i++) {
// While stack isn't empty AND current temp is warmer than
// the temperature at the index on top of the stack
while (!stack.isEmpty() &&
temperatures[i] > temperatures[stack.peek()]) {
int prevIndex = stack.pop(); // Get the waiting day
answer[prevIndex] = i - prevIndex; // Calculate wait time
}
// Current day joins the waiting list
stack.push(i);
}
return answer;
}
}
Let’s break this down:
We still have
result[]
initialized to 0’s.We use
Stack<Integer> stack
to hold indices of days waiting for a warmer day.We iterate
i
from 0 ton-1
. For each dayi
:While the stack isn’t empty and
temperatures[i]
is warmer thantemperatures[stack.peek()]
: pop the indexprevIndex
. Then we know dayi
is the next warmer day forprevIndex
, so setresult[prevIndex] = i - prevIndex
.After popping all colder days, we push
i
onto the stack (dayi
now waits for a future warmer day).
The core
while
loop corresponds exactly to our earlier description: “if the current temperature is greater than the temperature at the index on top of the stack, we pop and set the waiting time”. This loop might pop one or many indices if the new day is much warmer.Once we finish the loop, any indices still on the stack didn’t find a warmer day — but we already initialized those
result
entries to 0, so we’re done.
⏱️ Time Complexity: O(n) - Each index gets pushed and popped at most once 💾 Space Complexity: O(n) - In worst case, all indices could be in the stack
🎬 Walkthrough!
Let me walk you through this with temperatures = [73, 74, 75, 71, 69, 72, 76, 73]
:
Day 0 (73°F):
Stack is empty, so just push index 0
Stack: [0] (representing temperatures [73])
Answer: [0,0,0,0,0,0,0,0]
Day 1 (74°F):
74 > 73 (temp at stack.peek() which is index 0)
Pop index 0, set answer[0] = 1 - 0 = 1
Push index 1 onto stack
Stack: [1] (representing temperatures [74])
Answer: [1,0,0,0,0,0,0,0]
Day 2 (75°F):
75 > 74 (temp at index 1)
Pop index 1, set answer[1] = 2 - 1 = 1
Push index 2
Stack: [2] (representing temperatures [75])
Answer: [1,1,0,0,0,0,0,0]
Day 3 (71°F):
71 < 75 (temp at index 2), so no popping needed
Just push index 3
Stack: [2,3] (representing temperatures [75,71])
Answer: [1,1,0,0,0,0,0,0]
Day 4 (69°F):
69 < 71 (temp at index 3), so no popping
Push index 4
Stack: [2,3,4] (representing temperatures [75,71,69])
Answer: [1,1,0,0,0,0,0,0]
Day 5 (72°F):
72 > 69 (temp at index 4) → Pop 4, answer[4] = 5 - 4 = 1
72 > 71 (temp at index 3) → Pop 3, answer[3] = 5 - 3 = 2
72 < 75 (temp at index 2) → Stop popping, push 5
Stack: [2,5] (representing temperatures [75,72])
Answer: [1,1,0,2,1,0,0,0]
Day 6 (76°F) - The Big Moment:
76 > 72 (temp at index 5) → Pop 5, answer[5] = 6 - 5 = 1
76 > 75 (temp at index 2) → Pop 2, answer[2] = 6 - 2 = 4
Stack is empty, push 6
Stack: [6] (representing temperatures [76])
Answer: [1,1,4,2,1,1,0,0]
Day 7 (73°F):
73 < 76 (temp at index 6), so just push 7
Stack: [6,7] (representing temperatures [76,73])
Final Answer: [1,1,4,2,1,1,0,0]
The Beauty: Notice how day 6 (76°F) resolved TWO waiting days at once! This is the power of the monotonic stack - it can handle multiple resolutions in a single iteration.
The Bug That Haunted Me
I spent way too long debugging this mistake:
java
// ❌ WRONG - This will crash!
while (!stack.isEmpty() && stack.peek() < temperatures[i]) {
// This compares an INDEX to a TEMPERATURE - makes no sense!
}
// ✅ CORRECT
while (!stack.isEmpty() && temperatures[stack.peek()] < temperatures[i]) {
// This compares a TEMPERATURE to a TEMPERATURE - much better!
}
The mistake: stack.peek()
gives you an INDEX, not the temperature at that index. Always remember to use temperatures[stack.peek()]
when you need the actual temperature value.
This bug gave me an ArrayIndexOutOfBoundsException
that took me embarrassingly long to figure out. Don't be like past me - the stack stores indices, not values!
🚀 Performance Comparison
Let me put this in perspective with some rough numbers:
For n = 10,000 temperatures:
Brute Force: ~50,000,000 comparisons (n²/2)
Monotonic Stack: ~10,000 comparisons (each element pushed/popped once)
For n = 100,000 temperatures:
Brute Force: ~5,000,000,000 comparisons (good luck with that!)
Monotonic Stack: ~100,000 comparisons (still linear!)
🎯 Key Takeaways
Pattern Recognition is Everything: Once you see the "next greater element" pattern, monotonic stacks become your go-to tool.
Think About What You're Storing: We store indices, not values, because we need to calculate distances.
The While Loop is the Magic: That inner while loop can resolve multiple elements at once - that's where the efficiency comes from.
Each Element Has a Simple Life Cycle: Push onto stack → Wait for resolution → Get popped when resolved. Each element goes through this exactly once.
Visualization Helps: Draw out the stack states for small examples. It makes the pattern crystal clear.
Final Thoughts
When I first encountered this problem, I thought it was just about finding the next warmer day.
Instead of asking "For each day, what's the next warmer day?" (which leads to nested loops), we ask "For each day, which previous days is this the answer for?" (which leads to the monotonic stack).
This shift in perspective - from forward-looking to backward-resolving - is what transforms an O(n²) complexity into an O(n) complexity.
Subscribe to my newsletter
Read articles from Sabitha Paulraj directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
