LeetCode #503 Medium

Next Greater Element II

For each element in a circular array, find the next greater element, searching wrap-around; return -1 where none exists.

Constraints
  • 1 <= nums.length <= 10⁴
  • -10⁹ <= nums[i] <= 10⁹
monotonic-stackarray
Open on LeetCode ↗
02

Intuition

Next greater element ii finds, for each element, the next larger value to its right — but the array is circular, so the search wraps past the end back to the beginning. The non-circular version is a classic monotonic decreasing stack: walk the array holding indices whose next greater element is still unknown, and when a larger value arrives, it resolves every smaller index waiting on the stack. The circularity needs one adjustment, and there is a clean way to get it: - Iterate 2n times using i % n to index, which simulates walking the array twice without allocating a doubled copy. One full pass leaves some indices unresolved; the second pass lets values from the array's start resolve them. Anything still on the stack after both passes genuinely has no greater element anywhere, and stays −1. Actually concatenating the array to itself also works and is easier to picture, but doubles the memory for no benefit. One detail matters in the second pass: do not push indices during it. Every index was already pushed in the first pass, and pushing again creates duplicates that overwrite correct answers. The second pass only pops. The result array must be initialised to −1 so unresolved indices carry the right default without a final cleanup pass. The stack holds indices rather than values, since the answer must be written back to the correct position. Each index is pushed and popped at most once across both passes, so the cost stays O(n) despite the doubled loop.

How to spot this pattern

The circular version of next-greater. Walking 2n - 1 down to 0 with i % n simulates two laps, so every element sees the wrap-around; results are only recorded on the second lap, when the stack already holds everything to the right.

03

Approach

Try it first

Before reading on: price up what the direct approach costs here, then ask what ordering you can maintain so the answer is always at one end. Aim for O(n) time and O(n) space.

1

Use a monotonic decreasing stack

Hold indices whose next greater element is still unknown. A larger arriving value resolves every smaller index waiting, which is what makes one pass sufficient.

2

Simulate the wrap with modulo

Iterate 2n times indexing with i % n. This walks the array twice without allocating a doubled copy, letting early values resolve late indices.

3

Store indices, not values

The answer must be written to the correct position, so the stack holds indices. Values alone lose track of where each result belongs.

4

Do not push in the second pass

Every index was pushed during the first pass. Pushing again creates duplicates that overwrite correct answers — the second pass only pops.

5

Initialise results to -1

Indices never resolved keep the -1 default, which is exactly the required answer for elements with no greater value anywhere.

6

Cost of the approach

Each index is pushed and popped at most once across both passes, giving O(n) time and O(n) space despite the doubled loop.

04

Solution & live demo

▶1class Solution:
▶2 def nextGreaterElements(self, nums):
▶3 n = len(nums)
▶4 res = [-1] * n
▶5 st = []
▶6 for i in range(2 * n - 1, -1, -1):
▶7 cur = nums[i % n]
▶8 while st and st[-1] <= cur:
▶9 st.pop()
▶10 if i < n and st:
▶11 res[i] = st[-1]
▶12 st.append(cur)
▶13 return res
05

Common pitfalls

Recording results on both laps

✗ Wrong
if st: res[i % n] = st[-1]
✓ Right
if i < n and st:
    res[i] = st[-1]

During the first (higher-index) lap the stack hasn't yet seen the wrapped elements, so the answers are incomplete. The extra lap exists only to prime the stack; writes must wait until i is a real index.

Popping on strict greater-than

✗ Wrong
while st and st[-1] < cur:
✓ Right
while st and st[-1] <= cur:

"Next greater" is strict, so an equal value is not an answer and must be discarded. Keeping equals makes the stack report a same-valued element as the next greater one.

Doubling the array instead

✗ Wrong
nums = nums + nums
✓ Right
cur = nums[i % n]

Functionally the same but allocates a second array. The modulo indexing gets the identical traversal with no extra space, which matters when the array is large.

06

Edge cases

All elements equal

Strict > means nothing qualifies, so every answer is -1.

Strictly increasing array

The maximum gets -1 and everything else points to its right neighbour.

Single element

It cannot exceed itself, so the answer is [-1].

The maximum element

Always -1, since nothing anywhere in the circle is greater.

07

Complexity

Time
O(n)
Space
O(n)
2n iterations, each element pushed and popped a bounded number of times.