Next Greater Element IV
HARDDescription
You are given a 0-indexed array of non-negative integers nums. For each integer in nums, you must find its respective second greater integer.
The second greater integer of nums[i] is nums[j] such that:
j > inums[j] > nums[i]- There exists exactly one index
ksuch thatnums[k] > nums[i]andi < k < j.
If there is no such nums[j], the second greater integer is considered to be -1.
- For example, in the array
[1, 2, 4, 3], the second greater integer of1is4,2is3, and that of3and4is-1.
Return an integer array answer, where answer[i] is the second greater integer of nums[i].
Example 1:
Input: nums = [2,4,0,9,6] Output: [9,6,6,-1,-1] Explanation: 0th index: 4 is the first integer greater than 2, and 9 is the second integer greater than 2, to the right of 2. 1st index: 9 is the first, and 6 is the second integer greater than 4, to the right of 4. 2nd index: 9 is the first, and 6 is the second integer greater than 0, to the right of 0. 3rd index: There is no integer greater than 9 to its right, so the second greater integer is considered to be -1. 4th index: There is no integer greater than 6 to its right, so the second greater integer is considered to be -1. Thus, we return [9,6,6,-1,-1].
Example 2:
Input: nums = [3,3] Output: [-1,-1] Explanation: We return [-1,-1] since neither integer has any integer greater than it.
Constraints:
1 <= nums.length <= 1050 <= nums[i] <= 109
Approaches
Checkout 3 different approaches to solve Next Greater Element IV. Click on different approaches to view the approach and algorithm in detail.
Optimal Solution with Two Monotonic Stacks
This is the most efficient approach, achieving linear time complexity. It builds upon the idea of using a monotonic stack but employs a second stack instead of a priority queue to manage elements that are waiting for their second greater element. This avoids the logarithmic time complexity of priority queue operations.
Algorithm
- Initialize
answerarray of sizenwith-1. - Initialize two stacks,
s1ands2, to store indices. - For
ifrom0ton-1: - Let
currentNum = nums[i]. - While
s2is not empty andnums[s2.peek()] < currentNum: -
`answer[s2.pop()] = currentNum`. - Initialize a temporary stack
temp. - While
s1is not empty andnums[s1.peek()] < currentNum: -
`temp.push(s1.pop())`. - While
tempis not empty: s2.push(temp.pop()).- Push
iontos1. - Return
answer.
We use two stacks, s1 and s2. Both will store indices and will be maintained as monotonic stacks (elements with smaller values on top of elements with larger values).
s1: Stores indices of elements for which we are looking for the first greater element.s2: Stores indices of elements for which we have found the first greater element and are now looking for the second. We iterate through the arraynumswith indexi. For eachnums[i]:
- We first process
s2. Whiles2is not empty and the element at its topnums[s2.peek()]is smaller thannums[i], we have found the second greater element fors2.peek(). We pop the index and set its answer tonums[i]. - Then, we process
s1. Whiles1is not empty andnums[s1.peek()]is smaller thannums[i], we have found the first greater element. We pop these indices froms1. Since they now need to find their second greater element, they must be moved tos2. - A crucial detail is how to move elements from
s1tos2. When we pop multiple indices froms1, they come out in increasing order of their corresponding values. To maintains2as a monotonic (decreasing value) stack, we must push these indices ontos2in reverse order. We can use a temporary stack to achieve this reversal. - Finally, we push the current index
iontos1. This single pass through the array correctly identifies the second greater element for all indices in linear time.
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
class Solution {
public int[] secondGreaterElement(int[] nums) {
int n = nums.length;
int[] answer = new int[n];
Arrays.fill(answer, -1);
Deque<Integer> s1 = new ArrayDeque<>();
Deque<Integer> s2 = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
int currentNum = nums[i];
while (!s2.isEmpty() && nums[s2.peek()] < currentNum) {
answer[s2.pop()] = currentNum;
}
Deque<Integer> tempStack = new ArrayDeque<>();
while (!s1.isEmpty() && nums[s1.peek()] < currentNum) {
tempStack.push(s1.pop());
}
while(!tempStack.isEmpty()){
s2.push(tempStack.pop());
}
s1.push(i);
}
return answer;
}
}
Complexity Analysis
Pros and Cons
- Optimal time complexity. It's the most efficient way to solve this problem.
- The logic involving three stacks (including the temporary one) can be slightly more complex to reason about compared to the priority queue approach.
Code Solutions
Checking out 3 solutions in different languages for Next Greater Element IV. Click on different languages to view the code.
class Solution {
public
int[] secondGreaterElement(int[] nums) {
int n = nums.length;
int[] ans = new int[n];
Arrays.fill(ans, -1);
int[][] arr = new int[n][0];
for (int i = 0; i < n; ++i) {
arr[i] = new int[]{nums[i], i};
}
Arrays.sort(arr, (a, b)->a[0] == b[0] ? a[1] - b[1] : b[0] - a[0]);
TreeSet<Integer> ts = new TreeSet<>();
for (int[] pair : arr) {
int i = pair[1];
Integer j = ts.higher(i);
if (j != null && ts.higher(j) != null) {
ans[i] = nums[ts.higher(j)];
}
ts.add(i);
}
return ans;
}
}
Video Solution
Watch the video walkthrough for Next Greater Element IV
Similar Questions
5 related questions you might find useful
Algorithms:
Data Structures:
Subscribe to Scale Engineer newsletter
Learn about System Design, Software Engineering, and interview experiences every week.
No spam, unsubscribe at any time.