一、啥是下一个更大元素问题

咱先来说说下一个更大元素问题是啥。简单来讲,就是给你一个数组,对于数组里的每个元素,你要找出在它右边第一个比它大的元素。要是右边没有比它大的元素,那就用 -1 来表示。

举个例子哈,有个数组 [2, 1, 2, 4, 3]。对于第一个元素 2 来说,它右边第一个比它大的元素是 4;对于第二个元素 1 来说,右边第一个比它大的元素是 2;第三个元素 2 右边第一个比它大的元素也是 4;第四个元素 4 右边没有比它大的元素,所以是 -1;第五个元素 3 右边没有比它大的元素,也是 -1。所以这个数组对应的下一个更大元素数组就是 [4, 2, 4, -1, -1]

二、单调栈是个啥

单调栈是一种特殊的栈,它的特点是栈里的元素是单调递增或者单调递减的。咱们解决下一个更大元素问题用的是单调递减栈。啥意思呢,就是栈里的元素从栈底到栈顶是逐渐变小的。

当我们遍历数组的时候,如果当前元素比栈顶元素大,那就说明栈顶元素找到了它右边第一个比它大的元素,这时候就把栈顶元素出栈,并且记录下栈顶元素对应的下一个更大元素就是当前元素。然后继续比较当前元素和新的栈顶元素,直到当前元素小于等于栈顶元素,再把当前元素入栈。

三、单调栈解决下一个更大元素问题的代码实现(Java 技术栈)

import java.util.Arrays;
import java.util.Stack;

public class NextGreaterElement {
    public static int[] nextGreaterElements(int[] nums) {
        int n = nums.length;
        // 用于存储结果的数组,初始值都设为 -1
        int[] result = new int[n];
        Arrays.fill(result, -1);
        // 创建一个栈
        Stack<Integer> stack = new Stack<>();

        // 遍历数组
        for (int i = 0; i < n; i++) {
            // 当栈不为空且当前元素大于栈顶元素时
            while (!stack.isEmpty() && nums[stack.peek()] < nums[i]) {
                // 栈顶元素出栈
                int index = stack.pop();
                // 记录栈顶元素对应的下一个更大元素
                result[index] = nums[i];
            }
            // 当前元素入栈
            stack.push(i);
        }
        return result;
    }

    public static void main(String[] args) {
        int[] nums = {2, 1, 2, 4, 3};
        int[] result = nextGreaterElements(nums);
        // 输出结果
        for (int num : result) {
            System.out.print(num + " ");
        }
    }
}

代码解释

  1. 首先,我们创建了一个和原数组长度一样的结果数组 result,并且把里面的元素都初始化为 -1。这是因为如果一个元素右边没有比它大的元素,那它对应的下一个更大元素就是 -1。
  2. 然后,我们创建了一个栈 stack,用来存储数组元素的下标。
  3. 接着,我们开始遍历数组。对于每个元素,我们检查栈是否为空,并且当前元素是否大于栈顶元素对应的元素。如果是,就把栈顶元素出栈,并且记录下栈顶元素对应的下一个更大元素就是当前元素。
  4. 最后,把当前元素的下标入栈。

四、应用场景

单调栈解决下一个更大元素问题在很多场景都有用。比如说在股票交易中,我们可以用它来找出某一天之后第一个股票价格比当天高的日子。再比如说在计算柱状图中最大矩形面积的时候,也会用到这个思路。

五、技术优缺点

优点

  1. 时间复杂度低:单调栈的时间复杂度是 O(n),因为每个元素最多入栈和出栈一次。这比暴力解法的 O(n^2) 要快很多。
  2. 代码简洁:代码实现相对简单,只需要一个栈和一个循环就可以解决问题。

缺点

  1. 适用范围有限:单调栈主要适用于解决下一个更大元素、下一个更小元素这类问题,对于其他类型的问题可能不太适用。
  2. 理解难度较大:对于初学者来说,单调栈的概念和实现可能比较难理解。

六、注意事项

  1. 栈里存储的是下标:在实现单调栈的时候,栈里存储的是数组元素的下标,而不是元素本身。这样方便我们记录每个元素对应的下一个更大元素。
  2. 边界条件:在使用栈的时候,要注意栈为空的情况,避免出现空指针异常。

七、文章总结

通过单调栈,我们可以优雅地解决下一个更大元素问题。单调栈的核心思想就是利用栈的单调性,在遍历数组的过程中,不断地更新每个元素的下一个更大元素。它的时间复杂度低,代码简洁,但是适用范围有限,理解起来也有一定难度。在实际应用中,我们要根据具体的问题来选择合适的算法。