[H单调栈] lc3113. 边界元素是最大值的子数组数目(单调栈+问题转换+模板题+好题)

文章目录

    • [1. 题目来源](#1. 题目来源)
    • [2. 题目解析](#2. 题目解析)

1. 题目来源

链接:3113. 边界元素是最大值的子数组数目

2. 题目解析

单调栈问题,代码很短,看看是怎么一步步思考出来的:

  • 首先,子数组问题,可以优先考虑枚举 右端点(枚举左端点也是可以的)。枚举右端点的好处是,枚举到 rj 时,我的 li 已经被枚举过了,我只需要从 "满足条件" 的 li 中挑选出来符合的结果即可。这里的条件,实际上就是题目中的待发现的性质。
  • 对于本题,先举个例子:4, 3, 1, 2, 1 顺序枚举时,有 4,3,1,当遇见 2 时中间这个 1 是完全没有机会的,因为左边是比它大的数,右边也有个比它大的数,它已经不可能是一个子数组的边界了。
    • 所以,可以不用考虑这个 1。
    • 根据这个性质,很明显,只需要维护一个从左到右的单调栈就行了。
    • 栈内放什么元素呢?应该放的是 [数字, 数字出现的次数],因为题目要求的子数组两头是一样的数字。
    • 再发现,当我们有了单调栈,现在枚举到 x 时,x 和栈顶有三个关系。
      • x > top:直接出栈即可
      • x == top:答案累计 top 出现的次数即可,并需要将 top 的次数需要加 1。
      • x < top:入栈,次数为 1。

代码实现技巧:

  • res 初始化为 n,因为单个元素也算一种子数组。
  • 单调栈放入一个正无穷大哨兵,防止栈为空。就不用做 stk.size() 的判断了。
    • 这是一个很强的技巧哈,积累一下。

  • 时间复杂度 : O ( n ) O(n) O(n)
  • 空间复杂度 : O ( 1 ) O(1) O(1)

单调栈,未加哨兵:

cpp 复制代码
class Solution {
public:
    long long numberOfSubarrays(vector<int>& nums) {
        typedef long long LL;
        int n = nums.size();

        LL res = n;
        stack<pair<int, int>> stk;
        for (int i = 0; i < n; i ++ ) {
            int x = nums[i];
            while (stk.size() && stk.top().first < x) stk.pop();
            if (stk.size() && stk.top().first == x) {
                res += stk.top().second;
                stk.top().second ++ ;
                continue;
            } 

            stk.push({x, 1});
        }

        return res;
    }
};

单调栈,哨兵优化,避免判断栈是否为空:

cpp 复制代码
class Solution {
public:
    long long numberOfSubarrays(vector<int>& nums) {
        typedef long long LL;
        int n = nums.size();

        LL res = n;
        stack<pair<int, int>> stk;
        stk.push({1e9, 0}); // 添加哨兵元素,保证单调栈不为空
        for (int i = 0; i < n; i ++ ) {
            int x = nums[i];
            while (stk.top().first < x) stk.pop();
            if (stk.top().first == x)  res += stk.top().second ++ ;
            else stk.push({x, 1});
        }

        return res;
    }
};
相关推荐
土司大王2 分钟前
LeetCode hot100——移动零
java·算法·leetcode
Tyler_TXZ1 小时前
C++C语言之——树
c语言·c++·算法·
旖旎夜光1 小时前
LeetCode 30:串联所有单词的子串(滑动窗口) —— 题解
数据结构·c++·算法·leetcode·滑动窗口
白狐_7982 小时前
408 数据结构|完全二叉树两道典型题:第 6 层叶结点数与叶结点总数
数据结构·算法
lucas_AI2 小时前
喂张白纸也能吐出证件号?文档 MLLM 的"关系级泄露"被测出来了
人工智能·算法·掘金技术征文
手写码匠2 小时前
华为云Flexus+DeepSeek征文|Dify 多 Agent 灰度发布实战:让每一次变更都“小步快跑、随时可回滚“
人工智能·深度学习·算法·aigc
阿部多瑞 ABU2 小时前
从0到1:用 Spring Boot 3.4 + Vue3 做一个能“智能排道次“的运动会编排系统(附核心算法)
java·spring boot·后端·算法·spring
Nil2082 小时前
leetcode 48旋转图像
算法·leetcode·职场和发展
嘟嘟07173 小时前
顺时针螺旋填充 n×n 矩阵:手撕 generateMatrix 的四个 for 循环
javascript·算法·面试
Nil2083 小时前
leetcode 206反转链表
算法·leetcode·链表