A monotonic stack keeps its contents ordered by discarding elements that can
never be the answer again, which turns a quadratic scan into a linear one.
def next_greater(nums):
"""For each element, the next element to its right that is larger."""
result = [-1] * len(nums)
stack = []
for i, n in enumerate(nums):
while stack and nums[stack[-1]] < n:
result[stack.pop()] = n
stack.append(i)
return result
nums [2, 1, 5, 3]
i=0 stack [0] waiting: 2
i=1 stack [0,1] waiting: 2, 1
i=2 5 > 1 -> pop, result[1] = 5
5 > 2 -> pop, result[0] = 5
stack [2] waiting: 5
i=3 stack [2,3]
result [5, 5, -1, -1]
The efficiency argument is the same amortised one as the sliding window: each
index is pushed once and popped at most once, so the inner while cannot make
the whole thing quadratic even though it looks like it might.
What the stack is really holding is unanswered questions. An element stays
until something resolves it, and the monotonic property means once an element is
popped it can never be needed again — the value that popped it is closer and
larger, so it would have answered anything the popped element could. That
observation is the invariant, and it generalises to largest rectangle in a
histogram, daily temperatures and trapping rain water.