Next Greater Element I
For each element in nums1, find its next greater element in nums2 using a monotonic stack.
3 min read
hash-map monotonic-stack
Problem#
nums1 is a subset of nums2. For each nums1[i], find the next greater element to its right in nums2, or -1 if none.
Examples#
nums1 = [4,1,2], nums2 = [1,3,4,2]→[-1,3,-1].nums1 = [2,4], nums2 = [1,2,3,4]→[3,-1].
Constraints#
1 <= nums1.length <= nums2.length <= 1000. All elements innums2are unique.
Hints#
Hint
Precompute next-greater for every nums2 value with a single monotonic stack pass.
Approach#
Right-to-left scan nums2 keeping a stack of values seen so far. For each v, pop everything <= v; the top (if any) is the next greater. Store nextGreater[v] in a hash map. Then answer each nums1[i] in O(1).
Solution#
class Solution {public: vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) { unordered_map<int,int> nxt; stack<int> st; for (int i = (int)nums2.size() - 1; i >= 0; --i) { while (!st.empty() && st.top() <= nums2[i]) st.pop(); nxt[nums2[i]] = st.empty() ? -1 : st.top(); st.push(nums2[i]); } vector<int> ans; ans.reserve(nums1.size()); for (int v : nums1) ans.push_back(nxt[v]); return ans; }};func nextGreaterElement(nums1 []int, nums2 []int) []int { nxt := map[int]int{} st := []int{} for i := len(nums2) - 1; i >= 0; i-- { for len(st) > 0 && st[len(st)-1] <= nums2[i] { st = st[:len(st)-1] } if len(st) == 0 { nxt[nums2[i]] = -1 } else { nxt[nums2[i]] = st[len(st)-1] } st = append(st, nums2[i]) } ans := make([]int, 0, len(nums1)) for _, v := range nums1 { ans = append(ans, nxt[v]) } return ans}from typing import List
class Solution: def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: nxt: dict[int, int] = {} stack: list[int] = [] for i in range(len(nums2) - 1, -1, -1): while stack and stack[-1] <= nums2[i]: stack.pop() nxt[nums2[i]] = stack[-1] if stack else -1 stack.append(nums2[i]) return [nxt[v] for v in nums1]function nextGreaterElement(nums1, nums2) { const nxt = new Map(); const stack = []; for (let i = nums2.length - 1; i >= 0; i--) { while (stack.length && stack[stack.length - 1] <= nums2[i]) stack.pop(); nxt.set(nums2[i], stack.length ? stack[stack.length - 1] : -1); stack.push(nums2[i]); } return nums1.map(v => nxt.get(v));}class Solution { public int[] nextGreaterElement(int[] nums1, int[] nums2) { Map<Integer, Integer> nxt = new HashMap<>(); Deque<Integer> stack = new ArrayDeque<>(); for (int i = nums2.length - 1; i >= 0; i--) { while (!stack.isEmpty() && stack.peek() <= nums2[i]) stack.pop(); nxt.put(nums2[i], stack.isEmpty() ? -1 : stack.peek()); stack.push(nums2[i]); } int[] ans = new int[nums1.length]; for (int i = 0; i < nums1.length; i++) ans[i] = nxt.get(nums1[i]); return ans; }}function nextGreaterElement(nums1: number[], nums2: number[]): number[] { const nxt = new Map<number, number>(); const stack: number[] = []; for (let i = nums2.length - 1; i >= 0; i--) { while (stack.length && stack[stack.length - 1] <= nums2[i]) stack.pop(); nxt.set(nums2[i], stack.length ? stack[stack.length - 1] : -1); stack.push(nums2[i]); } return nums1.map(v => nxt.get(v)!);}Editorial#
A right-to-left monotonic decreasing stack answers “next greater to the right” in O(N) total. The hash map then bridges from the second array’s positions to the first array’s queries. Without the precompute, you’d be doing O(N * M) nested searches.
Complexity#
- Time: O(N + M).
- Space: O(N).
Concept revision#
Related#