496. Next Greater Element I - Explanation

Problem Link

Description

The next greater element of some element x in an array is the first greater element that is to the right of x in the array.

You are given two 0-indexed integer arrays nums1 and nums2, where nums1 is a subset of nums2. nums2 contains unique elements.

For each 0 <= i < nums1.length, find the index j such that nums1[i] == nums2[j] and determine the next greater element of nums2[j] in nums2. If there is no next greater element, then the answer for this query is -1.

Example 1:

Input: nums1 = [4,1,2], nums2 = [1,3,4,2]

Output: [-1,3,-1]

Explanation:

  • There is no next greater element for 4.
  • 3 is the next greater element for 1.
  • There is no next greater element for 2.

Example 2:

Input: nums1 = [2,4], nums2 = [1,2,3,4]

Output: [3,-1]

Explanation:

  • 3 is the next greater element for 2.
  • There is no next greater element for 4.

Constraints:

  • 1 <= nums1.length <= nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 10,000
  • All integers in nums1 and nums2 are unique.
  • All the integers of nums1 also appear in nums2.

Follow up: Could you find an O(nums1.length + nums2.length) solution?



Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Hash Maps - Mapping values to indices for O(1) lookups
  • Monotonic Stack - Maintaining a stack in decreasing order to find next greater elements efficiently
  • Array Traversal - Iterating through arrays from different directions

1. Brute Force

Intuition

For each element in nums1, we need to find it in nums2 and then look for the first larger element to its right. The simplest approach scans nums2 from right to left: track the largest element seen so far that is greater than our target. When we hit the target element, we have our answer.

This works but is inefficient since we repeat the scan for every element in nums1.

Algorithm

  1. For each number in nums1:
    • Scan nums2 from right to left.
    • Track the most recent element that is greater than the current number.
    • When we find the current number in nums2, stop and record the tracked greater element (or -1 if none found).
  2. Return the results.
class Solution:
    def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        n = len(nums2)
        res = []
        for num in nums1:
            nextGreater = -1
            for i in range(n - 1, -1, -1):
                if nums2[i] > num:
                    nextGreater = nums2[i]
                elif nums2[i] == num:
                    break
            res.append(nextGreater)
        return res

Time & Space Complexity

  • Time complexity: O(mn)O(m * n)
  • Space complexity: O(1)O(1)

Where mm is the size of the array nums1nums1 and nn is the size of the array nums2nums2.


2. Hash Map

Intuition

Instead of scanning from the end each time, we can iterate through nums2 from left to right. For each element that appears in nums1, we look ahead to find the next greater element. A hash map stores the index of each nums1 element, so we can quickly check if a number from nums2 is one we care about.

This is still O(m * n) in the worst case, but we skip elements not in nums1.

Algorithm

  1. Build a hash map that maps each element in nums1 to its index.
  2. Initialize result array with all -1 values.
  3. Iterate through nums2. For each element that exists in the hash map:
    • Scan forward to find the first greater element.
    • Store the result at the corresponding index.
  4. Return the result array.
class Solution:
    def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        nums1Idx = {num : i for i, num in enumerate(nums1)}
        res = [-1] * len(nums1)

        for i in range(len(nums2)):
            if nums2[i] not in nums1Idx:
                continue
            for j in range(i + 1, len(nums2)):
                if nums2[j] > nums2[i]:
                    idx = nums1Idx[nums2[i]]
                    res[idx] = nums2[j]
                    break
        return res

Time & Space Complexity

  • Time complexity: O(mn)O(m * n)
  • Space complexity: O(m)O(m)

Where mm is the size of the array nums1nums1 and nn is the size of the array nums2nums2.


3. Stack

Intuition

A monotonic stack solves this problem in linear time. We iterate through nums2 and maintain a stack of elements that have not yet found their next greater element. When we encounter a larger element, it becomes the "next greater" for all smaller elements on the stack.

The stack maintains decreasing order from bottom to top. When a new element is larger than the stack top, we pop elements and record the current element as their next greater. We only push elements that are in nums1 since those are the only ones we need answers for.

Algorithm

  1. Build a hash map that maps each element in nums1 to its index.
  2. Initialize result array with all -1 values.
  3. Iterate through nums2 with a stack:
    • While the stack is not empty and the current element is greater than the stack top:
      • Pop the top, find its index in nums1, and set result[index] to current element.
    • If current element is in nums1, push it onto the stack.
  4. Return the result array.
class Solution:
    def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        nums1Idx = {num : i for i, num in enumerate(nums1)}
        res = [-1] * len(nums1)

        stack = []
        for i in range(len(nums2)):
            cur = nums2[i]
            while stack and cur > stack[-1]:
                val = stack.pop()
                idx = nums1Idx[val]
                res[idx] = cur
            if cur in nums1Idx:
                stack.append(cur)
        return res

Time & Space Complexity

  • Time complexity: O(m+n)O(m + n)
  • Space complexity: O(m)O(m)

Where mm is the size of the array nums1nums1 and nn is the size of the array nums2nums2.


Common Pitfalls

Searching in Wrong Array

The next greater element must be found in nums2, to the right of where the element appears in nums2. Some solutions mistakenly look for the next greater element within nums1 or search to the left instead of right in nums2.

Pushing All Elements onto Stack

The stack optimization only needs to track elements from nums1 that are waiting for their next greater element. Pushing every element from nums2 onto the stack works but uses unnecessary space. Only push elements that exist in the nums1 lookup map.