Next Greater Element in C++ (Time Complexity: O(n))


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 same array.

Given an array of integers nums, return an array containing the indices of the next greater element for each number in nums.

If a number doesn't have a next greater element, denote that with -1.


Example 1:

Input: nums = [1, 3, 2, 4]
Output: [1, 3, 3, -1]
Explanation: The next greater element for each value of nums is as follows:
- next greater element for 1 is 3 which is located at index 1
- next greater element for 3 is 4 which is located at index 3
- next greater element for 2 is 4 which is located at index 3
- 4 is the last and doesn't have a next greater element so we mark it with -1

Example 2:

Input: nums = [7, 3, 2, 6, 11, 9, 8, 10, 13]
Output: [4, 3, 3, 4, 8, 7, 7, 8, -1]

Understanding the Problem

The core challenge of this problem is to efficiently find the next greater element for each element in the array. This problem is significant in various applications such as stock price analysis, where you might want to know the next higher price after a given day.

Potential pitfalls include misunderstanding the requirement to find the first greater element to the right and not just any greater element.

Approach

To solve this problem, we can use a stack to keep track of the indices of the elements for which we are trying to find the next greater element. This approach ensures that we only traverse the array once, achieving an O(n) time complexity.

Naive Solution

A naive solution would involve using two nested loops to compare each element with every other element to its right. This would result in an O(n^2) time complexity, which is not efficient for large arrays.

Optimized Solution

The optimized solution uses a stack to keep track of indices of elements for which we haven't found the next greater element yet. As we traverse the array, we compare the current element with the element at the index stored at the top of the stack. If the current element is greater, we pop the stack and record the current index as the next greater element for the popped index. We continue this until the stack is empty or the current element is not greater. Finally, we push the current index onto the stack.

Algorithm

  1. Initialize an empty stack and a result array filled with -1.
  2. Traverse the array from left to right.
  3. For each element, while the stack is not empty and the current element is greater than the element at the index stored at the top of the stack, pop the stack and set the result for the popped index to the current index.
  4. Push the current index onto the stack.
  5. After the loop, the result array contains the indices of the next greater elements.

Code Implementation

#include <vector>
#include <stack>
#include <iostream>

std::vector<int> nextGreaterElements(const std::vector<int>& nums) {
    int n = nums.size();
    std::vector<int> result(n, -1); // Initialize result array with -1
    std::stack<int> s; // Stack to store indices

    for (int i = 0; i < n; ++i) {
        // While stack is not empty and current element is greater than element at index stored at top of stack
        while (!s.empty() && nums[i] > nums[s.top()]) {
            result[s.top()] = i; // Set the result for the index at top of stack
            s.pop(); // Pop the stack
        }
        s.push(i); // Push current index onto the stack
    }

    return result;
}

int main() {
    std::vector<int> nums = {1, 3, 2, 4};
    std::vector<int> result = nextGreaterElements(nums);

    for (int index : result) {
        std::cout << index << " ";
    }
    return 0;
}

Complexity Analysis

The time complexity of this approach is O(n) because each element is pushed and popped from the stack at most once. The space complexity is also O(n) due to the stack and the result array.

Edge Cases

Potential edge cases include:

  • An empty array, which should return an empty result array.
  • An array with all elements in descending order, where all elements should have a next greater element of -1.

These edge cases are handled naturally by the algorithm.

Testing

To test the solution comprehensively, consider the following test cases:

  • Simple cases with small arrays.
  • Arrays with all elements in ascending or descending order.
  • Arrays with duplicate elements.

Using a testing framework like Google Test can help automate and manage these tests.

Thinking and Problem-Solving Tips

When approaching such problems, consider using data structures like stacks or queues that can help manage the elements efficiently. Practice solving similar problems to develop a deeper understanding of these techniques.

Conclusion

Understanding and solving the Next Greater Element problem is crucial for mastering array manipulation and stack usage. Practice and explore further to enhance your problem-solving skills.

Additional Resources