Monotonic Stack: Code Templates in 6 Languages
If you have not read the concept guide yet, start there for the intuition and complexity analysis. This page gives you the code you can memorize and adapt during an interview. The main template solves the next greater element problem, and the variations cover the flipped comparison, the left-to-right variant, and the circular array trick.
Main Template: Next Greater Element
Every element must find the first greater value to its right. The stack stores indices, not values, and holds only elements whose answer is still unknown. Each new value resolves the unanswered elements smaller than itself.
Use this for Next Greater Element I .
function nextGreaterElement(nums) {
const n = nums.length;
const result = new Array(n).fill(-1);
const stack = []; // indices still waiting for an answer
for (let i = 0; i < n; i++) {
// Any waiting element smaller than the current value
// finally has its next greater element right here.
while (stack.length > 0 && nums[i] > nums[stack[stack.length - 1]]) {
result[stack.pop()] = nums[i];
}
stack.push(i);
}
return result;
}Code Breakdown
Key Variables
stack: stores indices of elements whose next greater value is still unknown. Indices, never values, because you need the position to write intoresult.result: the answer array, prefilled with-1for elements that never find a greater value.nums[i]: the current element. It acts as the resolver for everything smaller that is already on the stack.
Visual Mechanism
graph TD
A["Current index i"] --> B{"Stack not empty and nums[i] > nums[top]?"}
B -- "Yes" --> C["Pop top. result[top] = nums[i]"]
C --> B
B -- "No" --> D["Push i onto the stack"]
D --> E["Move to i + 1"]
Critical Sections
The while condition is the whole pattern. It combines two checks: the stack contains something, and the current value beats the value above it. Only when both hold does the top element find its answer, so it gets popped and recorded. When the condition fails, either the stack is empty or the current value satisfies the sorted order, and the current index becomes the newest open candidate.
The push is what keeps the stack sorted. Everything that breaks the order has been popped above, so the current value always fits at the top. That ordering is also why the top is always the smallest open value, which is the only element the next arriving value can answer.
Initialization matters too. The -1 fill stays correct even when the whole array is sorted descending. An empty array returns an empty list without touching the loop, so there is no off-by-one to handle.
Variations
1. Next Smaller Element
Flip the comparison from > to < and the same skeleton answers the next smaller element instead. The stack now keeps an increasing order.
Use this for practice before Sum of Subarray Minimums , which is built on finding nearby smaller elements.
function nextSmallerElement(nums) {
const n = nums.length;
const result = new Array(n).fill(-1);
const stack = []; // indices still waiting
for (let i = 0; i < n; i++) {
while (stack.length > 0 && nums[i] < nums[stack[stack.length - 1]]) {
result[stack.pop()] = nums[i];
}
stack.push(i);
}
return result;
}The only difference is the operator, so this variant is a good memorization anchor. When a problem says “nearest smaller value to the right,” reuse the skeleton and switch the comparison.
2. Previous Greater Element
The same idea answers to the left. Instead of popping into the result, you pop the candidates that cannot be an answer for the current index, and whatever remains on top is the answer.
Use this to understand the boundary computation in Sum of Subarray Minimums .
function previousGreaterElement(nums) {
const n = nums.length;
const result = new Array(n).fill(-1);
const stack = [];
for (let i = 0; i < n; i++) {
// Candidates not greater than the current value are
// hopeless, they can never answer anyone behind them.
while (stack.length > 0 && nums[stack[stack.length - 1]] <= nums[i]) {
stack.pop();
}
result[i] = stack.length > 0 ? nums[stack[stack.length - 1]] : -1;
stack.push(i);
}
return result;
}The subtle part in this variant is the <= in the pop condition. Equal values are not strictly greater, so they cannot be the answer and must be removed from the stack along with smaller ones.
3. Circular Next Greater
Scan the array twice and the second pass simulates wrapping around. Anything still unanswered after the first lap can still resolve on the lap that follows the end.
Use this for Next Greater Element II .
function nextGreaterElements(nums) {
const n = nums.length;
const result = new Array(n).fill(-1);
const stack = [];
// Two laps give every element a shot at anything behind it.
for (let i = 0; i < 2 * n; i++) {
const cur = nums[i % n];
while (stack.length > 0 && nums[stack[stack.length - 1]] < cur) {
result[stack.pop()] = cur;
}
if (i < n) {
stack.push(i);
}
}
return result;
}The double pass must push each index only during the first lap, otherwise the same index sits on the stack twice. Values that never meet a larger one, like the maximum, keep their -1 through both laps.
The circular variant keeps the same
Now head to the practice problems to apply these templates to real interview questions.