[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;
    }
};
相关推荐
დ旧言~几秒前
专题八:背包问题
算法·leetcode·动态规划·推荐算法
_WndProc17 分钟前
C++ 日志输出
开发语言·c++·算法
薄荷故人_19 分钟前
从零开始的C++之旅——红黑树及其实现
数据结构·c++
努力学习编程的伍大侠31 分钟前
基础排序算法
数据结构·c++·算法
XiaoLeisj1 小时前
【递归,搜索与回溯算法 & 综合练习】深入理解暴搜决策树:递归,搜索与回溯算法综合小专题(二)
数据结构·算法·leetcode·决策树·深度优先·剪枝
Jasmine_llq1 小时前
《 火星人 》
算法·青少年编程·c#
闻缺陷则喜何志丹1 小时前
【C++动态规划 图论】3243. 新增道路查询后的最短距离 I|1567
c++·算法·动态规划·力扣·图论·最短路·路径
Lenyiin2 小时前
01.02、判定是否互为字符重排
算法·leetcode
鸽鸽程序猿2 小时前
【算法】【优选算法】宽搜(BFS)中队列的使用
算法·宽度优先·队列