Leetcode 3113 边界元素是最大值的子数组数目:单调栈

文章内容

https://leetcode.cn/problems/find-the-number-of-subarrays-where-boundary-elements-are-maximum/description/

一、题目解析

给定整数数组 nums,统计满足条件的子数组个数:子数组的首元素和尾元素都等于该子数组的最大值。

单元素子数组天然满足(最大值就是它自己)。对于多元素子数组,设首尾索引为 i 和 j(i ≤ j),条件等价于:nums[i] == nums[j],且 i 到 j 之间没有任何元素大于 nums[i]。

以 nums = [2, 2, 1, 2] 为例,合法子数组有 7 个:

plain text
[2] [2] [2] [2]           ← 四个单元素[2,2]                      ← 首尾 2=2,中间无更大值[2,2,1,2]                  ← 首尾 2=2,中间 2,1 都不大于 2[2,1,2]                    ← 首尾 2=2,中间 1 不大于 2

不合法的情况:[2,1](首 2 ≠ 尾 1)、[1,2](首 1 ≠ 尾 2)、[2,2,1](首 2 ≠ 尾 1)。

二、单调栈思路

2.1 什么情况下合法

对于索引 i,我们要找它右边所有"能看见"的等值元素 j。所谓"能看见",就是中间没有比 nums[i] 更大的元素挡着。一旦出现一个大于 nums[i] 的值,i 就被它"挡住"了——i 无法再和更右边的任何元素组成合法子数组。

这恰好符合单调递减栈的场景:维护一个递减序列,遇到更大的元素就把前面更小的全部弹出(因为被挡住了)。

2.2 栈里存什么

标准单调递减栈只存索引。但这题需要计数:i 和 j 之间可能隔着多个相等元素,每一个都能和 j 配对。如果每次都扫描栈,复杂度就退化了。

解法:栈中每个元素存 [索引, 计数]。计数的含义是"栈中这个位置代表了多少个连续可见的等值元素"。当新元素与栈顶相等时,不入新元素,而是累加栈顶的计数,同时把计数加到答案上——因为当前元素可以和栈顶代表的每一个等值元素配对。

单调栈过程

三、解题过程

3.1 三种情况

遍历数组,对每个 nums[i]:

情况 ①:大于栈顶。nums[i] 比栈顶大,说明栈顶被挡住,后面的元素栈顶都看不见了。循环弹出所有小于 nums[i] 的栈顶。

情况 ②:等于栈顶。nums[i] 和栈顶值相等,且中间没有更大的值阻挡(情况 ① 已经清掉了所有更小的)。此时 nums[i] 可以和栈顶代表的每一个等值元素配对:

  • 栈顶计数 +1(把当前元素合并进去)
  • 答案累加栈顶的新计数

情况 ③:小于栈顶。nums[i] 比栈顶小,不会挡住栈顶。作为新元素入栈,计数为 1。同时它自己作为一个单元素子数组,答案 +1。

3.2 完整推演

以 nums = [2, 2, 1, 2] 推一遍:

i=0, nums[0]=2:栈空,入栈 [0, 1],ans=1。栈:0, 1

i=1, nums[1]=2:栈顶值=2,相等。计数 1→2,ans+2→3。栈:0, 2

i=2, nums[2]=1:栈顶值=2,1<2,小于。入栈 [2, 1],ans+1→4。栈:[[0, 2], [2, 1]]

i=3, nums[3]=2:栈顶值=1,2>1,大于。弹出 [2, 1]。新栈顶值=2,相等。计数 2→3,ans+3→7。栈:0, 3

最终 ans = 7,匹配枚举结果。

四、代码实现

java
class Solution {    public long numberOfSubarrays(int[] nums) {        // 单调递减栈,存 [索引, 可见等值元素计数]        Deque<Integer[]> stack = new ArrayDeque<>();        long ans = 0;         for (int i = 0; i < nums.length; i++) {            // 情况①:当前元素更大,弹出被挡住的栈顶            while (!stack.isEmpty() && nums[stack.peekLast()[0]] < nums[i]) {                stack.pollLast();            }             if (!stack.isEmpty() && nums[stack.peekLast()[0]] == nums[i]) {                // 情况②:相等,合并计数,当前元素与栈顶代表的每个等值元素都配对                stack.peekLast()[1]++;                ans += stack.peekLast()[1];            } else {                // 情况③:小于或栈空,新元素入栈                stack.addLast(new Integer[]{i, 1});                ans++;            }        }         return ans;    }}

每个元素入栈一次、出栈一次,时间复杂度 O(n)。栈中存的是索引和计数,空间复杂度 O(n)。


本文由 AgentPlanFlow 生成

觉得不错?给作者点个赞吧

分类:题解

评论 (0)

无需注册,留言经站长审核后公开。

加载评论中…

Leetcode 3113 边界元素是最大值的子数组数目:单调栈 · Yinchen 的博客