496. Next Greater Element I - Explanation
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. 3is the next greater element for1.- There is no next greater element for
2.
Example 2:
Input: nums1 = [2,4], nums2 = [1,2,3,4]
Output: [3,-1]Explanation:
3is the next greater element for2.- There is no next greater element for
4.
Constraints:
1 <= nums1.length <= nums2.length <= 10000 <= nums1[i], nums2[i] <= 10,000- All integers in
nums1andnums2are unique. - All the integers of
nums1also appear innums2.
Follow up: Could you find an O(nums1.length + nums2.length) solution?
Topics
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
- For each number in
nums1:- Scan
nums2from 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-1if none found).
- Scan
- 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 resTime & Space Complexity
- Time complexity:
- Space complexity:
The output array is not counted towards space complexity, as the standard convention measures only auxiliary working space.
Where is the size of the array and is the size of the array .
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
- Build a hash map that maps each element in
nums1to its index. - Initialize result array with all
-1values. - 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.
- 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 resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the size of the array and is the size of the array .
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
- Build a hash map that maps each element in
nums1to its index. - Initialize result array with all
-1values. - Iterate through
nums2with 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 setresult[index]to current element.
- Pop the top, find its index in
- If current element is in
nums1, push it onto the stack.
- While the stack is not empty and the current element is greater than the stack top:
- 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 resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the size of the array and is the size of the array .
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.
Sign in to join the discussion