As we iterate, we maintain a stack where elements are always in monotonic order. Whenever the new element violates the order, we pop elements (resolving their "next greater/smaller") before pushing the new element.
Array [2,1,5,4,3]. Monotonic decreasing stack — when a larger element arrives, pop everything smaller (their NGE is now resolved) before pushing the new element.
🧠 Under the Hood — why the "nested loop" is O(n)
Charge each pop to the element being popped, not to the loop iteration doing the popping. An element can only be popped once, so all pops across the entire run cost at most n — even if one single iteration pops 10 elements. This amortized accounting is the same argument that makes sliding-window and union-find analyses work, and it's worth being able to state precisely in an interview.