leetcode
leetcode 2451 ~ 2500
每个元素为最大值的最大范围

每个元素为最大值的最大范围

难度:

标签:

题目描述

代码结果

运行时间: 164 ms, 内存: 33.3 MB


/*
 * Leetcode 2832: 每个元素为最大值的最大范围
 * 
 * 题目思路:
 * 给定一个数组,要求找到每个元素在数组中作为最大值的最大范围。也就是说,
 * 对于数组中的每个元素,找到包含它且它是最大值的最长子数组范围。
 * 
 * 方法:
 * 1. 我们可以使用单调栈来解决这个问题。
 * 2. 先从左到右遍历数组,找到每个元素左侧第一个比它大的元素的位置。
 * 3. 再从右到左遍历数组,找到每个元素右侧第一个比它大的元素的位置。
 * 4. 通过左侧和右侧第一个比它大的元素的位置,可以确定该元素为最大值的最大范围。
 */
import java.util.*;
import java.util.stream.*;

public class MaxRangeForEachElementStream {
    public int[][] maxRange(int[] nums) {
        int n = nums.length;
        int[] left = new int[n];
        int[] right = new int[n];
        Deque<Integer> stack = new ArrayDeque<>();

        // 找到每个元素左侧第一个比它大的元素的位置
        IntStream.range(0, n).forEach(i -> {
            while (!stack.isEmpty() && nums[stack.peek()] <= nums[i]) {
                stack.pop();
            }
            left[i] = stack.isEmpty() ? -1 : stack.peek();
            stack.push(i);
        });

        stack.clear(); // 清空栈用于下一次遍历

        // 找到每个元素右侧第一个比它大的元素的位置
        IntStream.iterate(n - 1, i -> i >= 0, i -> i - 1).forEach(i -> {
            while (!stack.isEmpty() && nums[stack.peek()] <= nums[i]) {
                stack.pop();
            }
            right[i] = stack.isEmpty() ? n : stack.peek();
            stack.push(i);
        });

        // 计算每个元素为最大值的最大范围
        return IntStream.range(0, n).mapToObj(i -> new int[]{left[i] + 1, right[i] - 1}).toArray(int[][]::new);
    }
}

解释

方法:

此题解的核心思路是利用一个单调递减栈来寻找每个元素作为最大值时的最大范围。遍历输入数组,对于每个元素,如果当前元素大于栈顶元素,则栈顶元素无法继续维持其为最大值的状态,需要从栈中移除,并更新结果数组中该元素的最大范围。如果栈为空,则表示当前元素左边没有更大的元素,其范围可以扩展到数组起始;如果栈不为空,则当前元素的范围是从栈顶元素的下一个位置到当前元素的位置。最后,遍历结束后,栈中剩余的元素表示它们的范围可以扩展到数组末尾。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么选择使用单调递减栈而不是其他类型的数据结构来解决这个问题?
单调递减栈的选择是为了有效地处理和记录数组中每个元素作为最大值时能够扩展的最大范围。这种栈结构能快速找到一个元素左边和右边第一个比它小的元素,因此可以直接确定该元素的有效范围。使用单调递减栈可以在一次遍历中解决问题,保证时间复杂度为 O(n),这是其他数据结构(如队列或普通列表)难以实现的。
🦆
在弹出栈顶元素时,你是如何确定该元素的最大范围的?具体的计算逻辑是什么?
当一个元素被弹出栈时,表示遇到了一个比栈顶元素大的新元素,这个新元素的索引是当前遍历到的索引。栈顶元素的右边界即为新元素的索引减一。如果栈为空,说明没有左边界,该元素可以扩展到数组的起始;如果栈不为空,栈顶元素的索引加一就是左边界。因此,被弹出元素的范围可以通过当前索引与左边界之间的差值来确定。
🦆
如果数组中存在重复元素会如何影响栈的操作和最终的输出结果?
如果数组中存在重复元素,算法的处理方式不会直接受到影响因为单调递减栈在遇到重复元素时,由于重复元素不大于栈顶元素,它们会被继续压入栈中。然而,在计算范围时,重复元素会导致它们的范围可能不会立即更新,直到遇到一个更大的元素。这可能导致多个相同的元素有相同的最大范围值。
🦆
在算法的最后处理栈中剩余元素时,为什么更新范围至数组末尾的计算公式是 `n - stack[-1] - 1`?
在算法最后处理剩余在栈中的元素时,这些元素的右边没有出现比它们更大的元素,因此它们的右边界是数组的末尾。栈中的每个元素都可以扩展到数组的末尾,但是需要减去左边界(即栈中前一个元素的索引加一)。因此,最大范围的计算公式是 `n - stack[-1] - 1`,其中 `n` 是数组的长度,`stack[-1]` 是当前元素左边第一个元素的索引。

相关问题