Skip to content

124. Next Greater Element II

๐Ÿ”— LeetCode Problem: 503. Next Greater Element II
๐Ÿ“Š Difficulty: Medium
๐Ÿท๏ธ Topics: Array, Stack, Monotonic Stack

Problem Statement

Given a circular integer array nums (i.e., the next element of nums[nums.length - 1] is nums[0]), return the next greater number for every element in nums.

The next greater number of a number x is the first greater number to its traversing-order next in the array, which means you could search circularly to find its next greater number. If it doesn't exist, return -1 for this number.

Example 1:

Input: nums = [1,2,1]
Output: [2,-1,2]
Explanation:
  For 1 at index 0: next greater is 2
  For 2 at index 1: no next greater (it's the max), so -1
  For 1 at index 2: search circularly, next greater is 2

Example 2:

Input: nums = [1,2,3,4,3]
Output: [2,3,4,-1,4]
Explanation:
  For 1: next greater is 2
  For 2: next greater is 3
  For 3 at index 2: next greater is 4
  For 4: no next greater (it's the max), so -1
  For 3 at index 4: search circularly, next greater is 4

Constraints: - 1 <= nums.length <= 10^4 - -10^9 <= nums[i] <= 10^9


๐ŸŒŸ Understanding the Problem

What's Different from Next Greater Element I?

Two KEY differences:

Difference 1: Circular Array

Next Greater Element I (Linear):
  nums = [1, 3, 4, 2]
  For 2: Look right โ†’ nothing โ†’ answer: -1

Next Greater Element II (Circular):
  nums = [1, 3, 4, 2]
  For 2: Look right โ†’ nothing
         Wrap around โ†’ 1, 3, 4
         Find 3 > 2 โ†’ answer: 3 โœ“

Difference 2: Duplicate Values Allowed

Next Greater Element I:
  - All values are UNIQUE (problem constraint)
  - Can use HashMap: value โ†’ next greater
  - nums1 = [4,1,2], nums2 = [1,3,4,2]
  - No duplicates in nums2!

Next Greater Element II:
  - Values CAN repeat!
  - Example: [1, 2, 1, 3]
            โ†‘     โ†‘
          Two 1's at different positions!
  - Cannot use HashMap (which position?)
  - Must track POSITIONS (indices), not just values

This second difference is CRITICAL and changes our approach!

Visual Understanding

Linear array (NGE I):
  [1, 3, 4, 2] โ†’ End
  No wrap, no duplicates

Circular array (NGE II):
  [1, 3, 4, 2] โ†’ wraps โ†’ [1, 3, 4, 2] โ†’ ...
   โ†‘______________|
  Wraps around, CAN have duplicates!

๐Ÿ’ก How to DISCOVER the Solution

Let me show you how to think through this problem step by step.


๐ŸŽฏ STEP 1: Start with What We Know

From Next Greater Element I, we learned:

Use a stack to find next greater elements:
  - Stack stores waiting elements (in decreasing order)
  - When larger element appears, pop and resolve
  - Scan LEFT to RIGHT

This works for linear arrays!

But now we have a circular array. What changes?


๐ŸŽฏ STEP 2: Understanding Circular

What does "circular" mean?

Array: [4, 5, 2, 3]

Linear search for next greater of 3:
  Look right โ†’ nothing โ†’ answer: -1

Circular search for next greater of 3:
  Look right โ†’ nothing
  Wrap to start โ†’ 4 (4 > 3) โ†’ answer: 4 โœ“

The array "loops back" to the beginning!

Another example:

Array: [1, 2, 3, 4, 5]

For 5 (last element):
  Linear: nothing right โ†’ -1
  Circular: wrap โ†’ 1, 2, 3, 4 (none > 5) โ†’ -1

For 3:
  Linear: 4, 5 โ†’ answer: 4
  Circular: same โ†’ answer: 4

Circular matters only when we reach the end!

๐ŸŽฏ STEP 3: First Idea - Make Array Circular

Naive thought:

"What if I just duplicate the array?"

nums = [1, 2, 3]
doubled = [1, 2, 3, 1, 2, 3]

Now search for next greater in doubled array!

Let's trace this:

Original: [1, 2, 1]
Doubled:  [1, 2, 1, 1, 2, 1]

For index 0 (value 1):
  Search in doubled from position 0: 1, 2, 1, 1, 2, 1
  Next greater: 2 โœ“

For index 1 (value 2):
  Search in doubled from position 1: 2, 1, 1, 2, 1
  Next greater: None found โ†’ -1 โœ“

For index 2 (value 1):
  Search in doubled from position 2: 1, 1, 2, 1
  Next greater: 2 โœ“

Works! But...

Problems with this approach:

1. We're creating a new array (extra space O(n))
2. We need to map indices back to original array
3. Inefficient for large arrays


๐ŸŽฏ STEP 4: Can We Simulate Circular Without Copying?

Key insight:

Instead of creating doubled = [1,2,3,1,2,3]

We can SIMULATE it using indices!

Index % n gives us circular behavior:
  i=0 โ†’ 0%3 = 0 โ†’ nums[0]
  i=1 โ†’ 1%3 = 1 โ†’ nums[1]
  i=2 โ†’ 2%3 = 2 โ†’ nums[2]
  i=3 โ†’ 3%3 = 0 โ†’ nums[0] (wraps!)
  i=4 โ†’ 4%3 = 1 โ†’ nums[1]
  i=5 โ†’ 5%3 = 2 โ†’ nums[2]

This is brilliant!

Process indices 0 to 2n-1:
  - Each index i, access nums[i % n]
  - Simulates going around the circle twice!

Why twice?
  - First pass: process all elements
  - Second pass: catch elements that need to wrap around


๐ŸŽฏ STEP 5: Why Process Twice?

Think about it:

Array: [5, 4, 3, 2, 1]

First pass (i=0 to 4):
  5: no greater found yet
  4: no greater found yet
  3: no greater found yet
  2: no greater found yet
  1: no greater found yet

  Stack at end: [5, 4, 3, 2, 1]

Second pass (i=5 to 9, wrapping):
  i=5 โ†’ nums[0]=5: still max
  i=6 โ†’ nums[1]=4: still no greater
  ...

All remain -1 โœ“ (correct!)

Another example (what goes wrong if we keep pushing):

Array: [1, 2, 1]

If we naively push in BOTH passes:

First pass (i=0 to 2):
  i=0: stack=[0]  (index 0)
  i=1: pop 0, stack=[1]  (index 1)
  i=2: stack=[1,2]  (indices 1 and 2)

Second pass (i=3 to 5) - if we keep pushing:
  i=3: stack=[1,2,3%3]=stack=[1,2,0]  (duplicate index 0!)
  i=4: stack=[1,2,0,1]  (duplicate index 1!)
  i=5: stack=[1,2,0,1,2]  (duplicate index 2!)

This is wrong! We're adding same indices multiple times!

The problem:

We can't push indices in the second pass!
We already have all indices from first pass.
Second pass is ONLY for resolving, not adding!


๐ŸŽฏ STEP 6: The Critical Question - Values or Indices?

Wait! In NGE I, we stored VALUES in the stack. Why change now?

This is a crucial question. Let me show you why values won't work here.


๐Ÿ” Let's Try with Values First (Like NGE I)

Attempt 1: Store values in stack

Array: [1, 2, 1]
Result: [-1, -1, -1]

First pass (i=0 to 2):
  i=0, value=1: stack=[1]
  i=1, value=2: 2>1, pop 1
                How do we update result? 
                We know 1's next greater is 2
                But which INDEX has value 1?
                Could be index 0 OR index 2! โŒ

PROBLEM: We have DUPLICATE values!
         We can't tell which position to update!

This is the key difference from NGE I:

NGE I:
  - All values are UNIQUE (problem constraint!)
  - value โ†’ position mapping is ONE-to-ONE
  - We can use HashMap: value โ†’ next greater
  - No ambiguity!

NGE II:
  - Values CAN be DUPLICATED (no uniqueness constraint!)
  - value โ†’ position mapping is ONE-to-MANY
  - If we store value 1, we don't know if it's index 0 or 2
  - AMBIGUOUS! โŒ

๐ŸŽฏ Why Indices Solve This Problem

Attempt 2: Store indices in stack

Array: [1, 2, 1]
indices: 0  1  2

First pass (i=0 to 2):
  i=0: stack=[0]  โ† This is INDEX 0 (value nums[0]=1)

  i=1: stack.peek()=0, nums[0]=1 < nums[1]=2? YES
       Pop index 0
       result[0] = 2  โ† We know EXACTLY which position! โœ“
       stack=[1]

  i=2: nums[1]=2 < nums[2]=1? NO
       stack=[1, 2]

Now in second pass:
  i=4: nums[i%n] = nums[1] = 2
       stack.peek()=2, nums[2]=1 < 2? YES
       Pop index 2
       result[2] = 2  โ† We know it's position 2, not 0! โœ“

Why indices work:

1. Index uniquely identifies a POSITION
2. Index 0 is different from index 2 (even if values are same)
3. When we pop index k, we update result[k]
4. No ambiguity!


๐Ÿ“Š The Fundamental Difference

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                 NGE I          โ”‚         NGE II          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Values        UNIQUE           โ”‚     CAN DUPLICATE       โ”‚
โ”‚               (constraint)     โ”‚                         โ”‚
โ”‚                                โ”‚                         โ”‚
โ”‚ What we      element โ†’ next    โ”‚   position โ†’ next      โ”‚
โ”‚ need         greater            โ”‚   greater              โ”‚
โ”‚                                โ”‚                         โ”‚
โ”‚ Storage      VALUES work โœ“     โ”‚   VALUES fail โŒ       โ”‚
โ”‚ in stack                       โ”‚   INDICES needed โœ“     โ”‚
โ”‚                                โ”‚                         โ”‚
โ”‚ Why?         One value =       โ”‚   One value =          โ”‚
โ”‚              One position      โ”‚   Multiple positions   โ”‚
โ”‚                                โ”‚                         โ”‚
โ”‚ Lookup       HashMap           โ”‚   Direct array access  โ”‚
โ”‚              value โ†’ next      โ”‚   result[index] = next โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐ŸŽช Visual Example - Why Values Fail

Array: [3, 1, 2, 1, 4]
         โ†‘     โ†‘  โ†‘
      index 0  2  3  (all have duplicates!)

If we store VALUES:
  Stack: [3, 1]

  When we see 2:
    Pop value 1 โ†’ result[?] = 2

    Question: Which index?
      - Index 1 has value 1
      - Index 3 has value 1

    We CAN'T tell! โŒ

If we store INDICES:
  Stack: [0, 1]  โ† indices, not values!

  When we see 2 at index 2:
    Pop index 1 โ†’ result[1] = 2 โœ“

    No confusion! Index 1 is index 1.
    Even if index 3 also has value 1, we're clear!

๐Ÿง  The Deep Understanding

In NGE I:

Question: "What is the next greater element for VALUE 5?"
Answer: Can use HashMap because values are unique
        HashMap<Integer, Integer> map
        map.put(value, nextGreater)

In NGE II:

Question: "What is the next greater element at POSITION 2?"
Answer: Must track positions because values repeat
        int[] result = new int[n]
        result[index] = nextGreater

This is why:

NGE I:  Stack<Integer> stack  โ† stores VALUES
        Pop value โ†’ map.put(value, current)

NGE II: Stack<Integer> stack  โ† stores INDICES
        Pop index โ†’ result[index] = current


๐Ÿ’ก How to Access Values When Storing Indices

"But if I store index, how do I compare values?"

When we store index in stack:

  Stack: [2]  โ† This is index 2

  To compare with current:
    stack.peek() = 2  โ† This is an index
    nums[stack.peek()] = nums[2]  โ† This is the value!

    Compare: nums[stack.peek()] < currentValue

Example:
  stack = [2]  (index 2)
  nums = [1, 2, 1]
  current = nums[4%3] = nums[1] = 2

  Compare: nums[stack.peek()] < current
          nums[2] < 2
          1 < 2? YES!

So we get BOTH:

index = stack.peek()          โ† Which position (for result array)
value = nums[stack.peek()]    โ† What value (for comparison)

This is the power of storing indices!


โœ… Summary: Why Indices in NGE II

The reasoning chain:

  1. Problem: Array can have duplicate values
  2. Consequence: Can't use value โ†’ next greater mapping
  3. Solution: Must track positions (indices)
  4. Implementation: Store indices in stack
  5. Access pattern:
  6. Position: index = stack.peek()
  7. Value: value = nums[index]
  8. Update: result[index] = nextGreater

This is not arbitrary! It's the ONLY way to handle duplicates correctly!


๐ŸŽฏ STEP 7: The Algorithm Takes Shape

Initialize:
  result = array of -1s (default answer)
  stack = empty (stores indices)

Process i from 0 to 2n-1:
  current = nums[i % n]  (actual value, wrapping)

  While stack not empty AND nums[stack.top] < current:
    idx = stack.pop()
    result[idx] = current  (found next greater!)

  if i < n:  (only push in first pass)
    stack.push(i)

Why if i < n?

We only want each index in stack ONCE.
Second pass (i >= n) is just for resolving.


๐ŸŽฏ STEP 8: Visual Walkthrough

Array: [1, 2, 1]
n = 3, process i from 0 to 5

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=0, nums[0]=1
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

current = 1
Stack: []
While: stack empty, skip
i < n? 0 < 3? YES
Action: push(0)

Stack: [0]
Result: [-1, -1, -1]

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=1, nums[1]=2
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

current = 2
Stack: [0]
While: nums[stack.top] < current?
       nums[0]=1 < 2? YES
  Pop 0, result[0] = 2 โœ“

Stack: []
i < n? 1 < 3? YES
Action: push(1)

Stack: [1]
Result: [2, -1, -1]

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=2, nums[2]=1
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

current = 1
Stack: [1]
While: nums[1]=2 < 1? NO, skip
i < n? 2 < 3? YES
Action: push(2)

Stack: [1, 2]
Result: [2, -1, -1]

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ First Pass Complete โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Stack still has: [1, 2]
Meaning: indices 1 and 2 haven't found next greater yet

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=3, nums[3%3]=nums[0]=1
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

current = 1
Stack: [1, 2]
While: nums[2]=1 < 1? NO, skip
i < n? 3 < 3? NO, don't push

Stack: [1, 2]
Result: [2, -1, -1]

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=4, nums[4%3]=nums[1]=2
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

current = 2
Stack: [1, 2]
While: nums[2]=1 < 2? YES
  Pop 2, result[2] = 2 โœ“

Stack: [1]
While: nums[1]=2 < 2? NO, stop
i < n? 4 < 3? NO, don't push

Stack: [1]
Result: [2, -1, 2]

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=5, nums[5%3]=nums[2]=1
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

current = 1
Stack: [1]
While: nums[1]=2 < 1? NO, skip
i < n? 5 < 3? NO

Stack: [1]
Result: [2, -1, 2]

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
Final Result
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

Stack: [1] (index 1 has no next greater)
Result: [2, -1, 2] โœ“

Interpretation:
  nums[0]=1 โ†’ next greater is 2
  nums[1]=2 โ†’ no next greater (it's the max)
  nums[2]=1 โ†’ next greater is 2 (wrapped around!)

๐ŸŽฏ STEP 9: Why This Works

The magic of processing 2n elements:

First n elements (i=0 to n-1):
  - Build the stack with all indices
  - Resolve some (those with next greater in normal order)

Next n elements (i=n to 2n-1):
  - Don't add to stack (already have all indices)
  - Just resolve remaining (those that need wrap-around)

After 2n iterations:
  - All elements that CAN be resolved, ARE resolved
  - Elements still in stack โ†’ truly no next greater

Why we're guaranteed to find answer:

If element X has a next greater:
  - It's somewhere in the array
  - By going around twice, we WILL encounter it

If we don't find it after 2n iterations:
  - X is the maximum (or tied for maximum)
  - Correctly returns -1


๐ŸŽฏ Approach 1: Brute Force

Implementation

public int[] nextGreaterElements(int[] nums) {
    int n = nums.length;
    int[] result = new int[n];

    for (int i = 0; i < n; i++) {
        result[i] = -1;  // Default: not found

        // Search circularly
        for (int j = 1; j < n; j++) {
            int nextIdx = (i + j) % n;  // Wrap around
            if (nums[nextIdx] > nums[i]) {
                result[i] = nums[nextIdx];
                break;
            }
        }
    }

    return result;
}

Complexity Analysis

โฐ Time: O(nยฒ)

For each element: O(n)
  Search next n-1 elements: O(n)

๐Ÿ’พ Space: O(1) - excluding output


๐ŸŽฏ Approach 2: Monotonic Stack (Optimal)

The Strategy

1. Initialize result array with -1
2. Use stack to store indices (not values!)
3. Process indices 0 to 2n-1 (go around twice)
4. Use i%n to get actual array index (simulates circular)
5. Pop and resolve when larger element found
6. Only push indices in first pass (i < n)

Implementation

public int[] nextGreaterElements(int[] nums) {
    int n = nums.length;
    int[] result = new int[n];
    Arrays.fill(result, -1);  // Default: no next greater

    Stack<Integer> stack = new Stack<>();  // Stores indices

    // Process 2n elements (go around twice)
    for (int i = 0; i < 2 * n; i++) {
        int num = nums[i % n];  // Get actual value (circular)

        // Pop all elements smaller than current
        while (!stack.isEmpty() && nums[stack.peek()] < num) {
            result[stack.pop()] = num;
        }

        // Only push in first pass
        if (i < n) {
            stack.push(i);
        }
    }

    return result;
}

Detailed Dry Run

Input: nums = [1, 2, 3, 4, 3]
n = 5

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
Initialization
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

result = [-1, -1, -1, -1, -1]
stack = []

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=0, nums[0%5]=1
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

While: stack empty, skip
i < n? 0 < 5? YES
Push: stack = [0]

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=1, nums[1%5]=2
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

While: nums[0]=1 < 2? YES
  Pop 0, result[0] = 2
  stack = []

Push: stack = [1]
result = [2, -1, -1, -1, -1]

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=2, nums[2%5]=3
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

While: nums[1]=2 < 3? YES
  Pop 1, result[1] = 3
  stack = []

Push: stack = [2]
result = [2, 3, -1, -1, -1]

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=3, nums[3%5]=4
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

While: nums[2]=3 < 4? YES
  Pop 2, result[2] = 4
  stack = []

Push: stack = [3]
result = [2, 3, 4, -1, -1]

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=4, nums[4%5]=3
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

While: nums[3]=4 < 3? NO
Push: stack = [3, 4]
result = [2, 3, 4, -1, -1]

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ First Pass (i=0 to 4) Complete โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Stack: [3, 4] (indices 3 and 4 still waiting)

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=5, nums[5%5]=nums[0]=1
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

While: nums[4]=3 < 1? NO
i < n? 5 < 5? NO, don't push

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=6, nums[6%5]=nums[1]=2
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

While: nums[4]=3 < 2? NO
i < n? NO

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=7, nums[7%5]=nums[2]=3
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

While: nums[4]=3 < 3? NO
i < n? NO

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=8, nums[8%5]=nums[3]=4
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

While: nums[4]=3 < 4? YES
  Pop 4, result[4] = 4
  stack = [3]

While: nums[3]=4 < 4? NO
i < n? NO

result = [2, 3, 4, -1, 4]

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
i=9, nums[9%5]=nums[4]=3
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

While: nums[3]=4 < 3? NO
i < n? NO

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
Final Result
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

Stack: [3] (index 3 has no next greater)
Result: [2, 3, 4, -1, 4] โœ“

Verification:
  nums[0]=1 โ†’ 2 โœ“
  nums[1]=2 โ†’ 3 โœ“
  nums[2]=3 โ†’ 4 โœ“
  nums[3]=4 โ†’ -1 โœ“ (max element)
  nums[4]=3 โ†’ 4 โœ“ (wrapped to find 4)

Complexity Analysis

โฐ Time: O(n)

Process 2n elements: O(n)
Each element pushed once: O(n)
Each element popped once: O(n)
Total: O(n) โœ“

Even though we loop 2n times, it's still O(n)!

๐Ÿ’พ Space: O(n)

Stack: O(n) worst case
Result: O(n) (required output)


๐Ÿ“Š Approach Comparison

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚             Approach 1        Approach 2               โ”‚
โ”‚             (Brute Force)     (Monotonic Stack)        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Core Idea   For each, scan   Process 2n with          โ”‚
โ”‚             circularly        stack                    โ”‚
โ”‚                                                        โ”‚
โ”‚ Circular    (i+j) % n         i % n โœ“                 โ”‚
โ”‚ handling                                               โ”‚
โ”‚                                                        โ”‚
โ”‚ Time        O(nยฒ)             O(n) โœ“                   โ”‚
โ”‚                                                        โ”‚
โ”‚ Space       O(1)              O(n)                     โ”‚
โ”‚                                                        โ”‚
โ”‚ Interview   For small n       Always! โœ“               โ”‚
โ”‚             only                                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿงช Edge Cases

Edge Case 1: All Same Elements

Input: [3, 3, 3, 3]
Output: [-1, -1, -1, -1]

All equal, no greater exists

Edge Case 2: Strictly Increasing

Input: [1, 2, 3, 4, 5]
Output: [2, 3, 4, 5, -1]

Each has next greater except last

Edge Case 3: Strictly Decreasing

Input: [5, 4, 3, 2, 1]
Output: [-1, -1, -1, -1, -1]

All decreasing, no next greater

Edge Case 4: Single Element

Input: [1]
Output: [-1]

Only one element, circular doesn't help

Edge Case 5: Two Elements

Input: [1, 2]
Output: [2, -1]

1 โ†’ 2
2 โ†’ nothing greater (even circularly)

โš ๏ธ Common Mistakes

Mistake 1: Not processing 2n elements

// โŒ WRONG - Only one pass
for (int i = 0; i < n; i++) {
    // Won't catch wrap-around cases!
}

// โœ“ CORRECT - Two passes
for (int i = 0; i < 2 * n; i++) {
    // Catches all cases
}

Mistake 2: Storing values instead of indices

// โŒ WRONG - Can't update result array
stack.push(nums[i % n]);

// โœ“ CORRECT - Store indices
stack.push(i);  // In first pass only

Mistake 3: Pushing in second pass

// โŒ WRONG - Pushes duplicate indices
for (int i = 0; i < 2 * n; i++) {
    stack.push(i % n);  // Will push same indices twice!
}

// โœ“ CORRECT - Only push in first pass
if (i < n) {
    stack.push(i);
}

Mistake 4: Wrong circular index

// โŒ WRONG - Doesn't wrap
int num = nums[i];  // Out of bounds when i >= n

// โœ“ CORRECT - Use modulo
int num = nums[i % n];

๐ŸŽฏ Pattern Recognition

This pattern appears when:

- Array is circular/cyclic
- Need to find next greater/smaller in circular array
- Problems involving rotation or wrap-around

Key Technique:

Process 2n elements with i % n
  - First pass: build stack
  - Second pass: resolve wrap-around cases
  - Only push indices in first pass (i < n)

Similar Problems: - Find K Closest Elements (circular) - Maximum in Circular Array - Jump Game (circular variant)


๐Ÿ“ Quick Revision Notes

๐ŸŽฏ Core Concept

Circular array = can wrap around to beginning. Use monotonic stack with 2n iterations and i%n indexing to simulate circular behavior. Store indices in stack (not values). Only push in first pass (i < n).

โšก Quick Implementation

import java.util.Arrays;
import java.util.HashMap;
import java.util.Stack;

class Solution {
  public int[] nextGreaterElements(int[] a) {
    int[] res = new int[a.length];
    Arrays.fill(res, -1);
    // Approach 4 of Next Greater Element 1 with below changes
    // 1. store indices in map instead of values as duplicates are allowed in this example.
    // 2. You need to consider doubling the array virtually (not physically really)
    // 3. Since doubling is virtual, use mod.
    // 4. Limit stack pushing.
    HashMap<Integer, Integer> map = new HashMap<>();
    Stack<Integer> stack = new Stack<>();
    for(int ii = 0; ii < 2 * a.length; ii++) { // change 2
      int i = ii % a.length; // change 3
      // simply push if stack is empty.
      if(stack.isEmpty()) {
        stack.push(i); // change 1
      } else if (a[stack.peek()] > a[i]) {
        if(ii < a.length) { // change 4. Else, it keeps on increasing.
          stack.push(i);
        }        
      } else {
          while (!stack.isEmpty() && a[stack.peek()] < a[i]) {
            // in [1,2,1], when we are at element 2 and stack has 0 (index rather than value)
            // We found a[0] < a[1]. Update res.
            // Similarly, [4,3,2,5], when we are at element 5, stack has (0,1,2). We update 
            // values res[2], res[1] and res[0]
            res[stack.pop()] = a[i];
          }

          stack.push(i);
      }
    }

    return res;
  }

  public static void main(String[] args) {
    Solution s = new Solution();
    System.out.println(Arrays.toString(s.nextGreaterElements(new int[] {1,2,1})));
    System.out.println(Arrays.toString(s.nextGreaterElements(new int[] {1,2,3,4,3})));
  }
}

๐Ÿ”‘ Key Insights

Why 2n iterations? - First n: process all elements normally - Second n: catch wrap-around cases - Guarantees we find next greater if it exists

Why i % n? - Simulates circular array - i=0โ†’nums[0], i=3โ†’nums[0] (if n=3) - No need to duplicate array!

Why store indices? - Need to know which result[i] to update - Need to access nums[index] for comparison - Indices give us both!

Why i < n check? - Only want each index once in stack - Second pass just resolves, doesn't add new

๐ŸŽช Memory Aid

"Go around TWICE (2n iterations)"
"Use % n to WRAP (circular)"
"Store INDICES (not values)"
"Push only FIRST pass (i < n)" โœจ

โš ๏ธ Don't Forget

  • Loop from 0 to 2*n (not just n!)
  • Access array with nums[i % n] (not just i)
  • Store indices in stack (not values)
  • Only push when i < n
  • Initialize result with -1

Happy coding! Master this circular technique - it's very useful! ๐Ÿš€