Next Greater Element in JavaScript (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, temperature monitoring, and other scenarios where future values are compared to current values.

Potential pitfalls include misunderstanding the requirement to find the first greater element to the right and not just any greater element. Misconceptions may arise from confusing the index of the next greater element with the value itself.

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.

Here is a step-by-step breakdown of the approach:

  1. Initialize an empty stack and an array result with the same length as nums, filled with -1.
  2. Iterate through the array nums.
  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, update the result array for the index at the top of the stack with the current index, and pop the stack.
  4. Push the current index onto the stack.
  5. After the iteration, the result array will contain the indices of the next greater elements or -1 if there is no greater element.

Algorithm

Here is the detailed algorithm:

// Function to find the next greater element indices
function nextGreaterElementIndices(nums) {
    // Initialize the result array with -1
    const result = new Array(nums.length).fill(-1);
    // Initialize an empty stack
    const stack = [];
    
    // Iterate through the array
    for (let i = 0; i < nums.length; i++) {
        // While stack is not empty and the current element is greater than the element at the index stored at the top of the stack
        while (stack.length > 0 && nums[i] > nums[stack[stack.length - 1]]) {
            // Pop the index from the stack and update the result array
            const index = stack.pop();
            result[index] = i;
        }
        // Push the current index onto the stack
        stack.push(i);
    }
    
    return result;
}

// Example usage
const nums1 = [1, 3, 2, 4];
console.log(nextGreaterElementIndices(nums1)); // Output: [1, 3, 3, -1]

const nums2 = [7, 3, 2, 6, 11, 9, 8, 10, 13];
console.log(nextGreaterElementIndices(nums2)); // Output: [4, 3, 3, 4, 8, 7, 7, 8, -1]

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: The result should be an empty array.
  • An array with all elements in descending order: All elements should have -1 as their next greater element.
  • An array with all elements the same: All elements should have -1 as their next greater element.

Examples:

// Edge case: empty array
console.log(nextGreaterElementIndices([])); // Output: []

// Edge case: descending order
console.log(nextGreaterElementIndices([5, 4, 3, 2, 1])); // Output: [-1, -1, -1, -1, -1]

// Edge case: all elements the same
console.log(nextGreaterElementIndices([2, 2, 2, 2])); // Output: [-1, -1, -1, -1]

Testing

To test the solution comprehensively, include a variety of test cases:

  • Simple cases with small arrays.
  • Edge cases as discussed above.
  • Large arrays to test performance.

Use testing frameworks like Jest or Mocha for automated testing.

Thinking and Problem-Solving Tips

When approaching such problems:

  • Understand the problem requirements and constraints thoroughly.
  • Think about different approaches and their time complexities.
  • Use data structures like stacks or queues to optimize the solution.
  • Practice similar problems to improve problem-solving skills.

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 skills.

Additional Resources