2025.11.10 力扣每日一题

3542.将所有元素变为0的最少操作次数

cpp 复制代码
class Solution {
public:
    int minOperations(vector<int>& nums) {
       vector<int> s;
       int res = 0;
       for(int a : nums){
        while(!s.empty() && s.back() > a){
            s.pop_back();
        }
        if( a==0) continue;
        if(s.empty() || s.back() < a){
            ++res;
            s.push_back(a);
        }
       }
       return res;
    }
};

单线栈知识点:

单调栈是一种特殊的栈数据结构 ,其核心特性是:栈内的元素始终保持单调递增单调递减的顺序。这种特性使得它在解决 "找下一个更大 / 更小元素""区间最值" 等问题时效率极高(时间复杂度通常为 O (n))。

核心概念

  • 单调性:栈内元素从栈底到栈顶严格遵循递增(或递减)规则。
  • 操作原则:当新元素入栈时,若破坏单调性,则弹出栈顶元素,直到栈满足单调性后再入栈。
  • 用途:高效寻找数组中每个元素的 "下一个更大元素""上一个更小元素" 等,避免暴力遍历(O (n²))。

分类

  1. 单调递增栈 :栈内元素从栈底到栈顶从小到大 排列。例如:[1, 3, 5, 7]

  2. 单调递减栈 :栈内元素从栈底到栈顶从大到小 排列。例如:[7, 5, 3, 1]

工作流程(以单调递增栈为例)

假设数组为 [2, 1, 3],模拟入栈过程:

  1. 元素 2 入栈,栈为 [2](满足递增)。
  2. 新元素 1 比栈顶 2 小,破坏递增性,弹出 2,栈为空后入栈 1,栈为 [1]
  3. 新元素 3 比栈顶 1 大,直接入栈,栈为 [1, 3](满足递增)。

最终栈保持递增,且过程中通过弹出元素可记录 "被弹出元素的下一个更大元素是当前新元素"(如 2 的下一个更大元素是 3)。

典型应用

  1. 下一个更大元素 问题:对数组中每个元素,找到右侧第一个比它大的元素,若不存在则为 -1。示例:nums = [2, 1, 2, 4, 3]答案:[4, 2, 4, -1, -1]解法:用单调递减栈,遍历数组时,弹出栈顶元素并记录当前元素为其 "下一个更大元素"。

  2. 接雨水 问题:计算数组中 "凹槽" 能接住的雨水量(如 [0,1,0,2,1,0,1,3,2,1,2,1] 答案为 6)。解法:用单调递减栈,栈内存储 "凹槽左边界",遇到更高元素时计算凹槽面积。

  3. 柱状图中最大的矩形 问题:找到柱状图中能容纳的最大矩形面积(如 [2,1,5,6,2,3] 答案为 10)。解法:用单调递增栈,栈内存储柱子索引,遇到更矮柱子时弹出并计算以弹出柱子为高的最大矩形。

优势

  • 时间效率:每个元素最多入栈和出栈一次,总时间复杂度为 O (n)。
  • 简洁性:通过栈的单调性,可在遍历过程中同步记录所需信息,避免嵌套循环。

总结

单调栈的核心是利用 "栈的单调性" 过滤无效元素,只保留对后续计算有价值的元素,从而高效解决各类区间相关问题。掌握它的关键是理解 "何时入栈、何时出栈" 以及 "弹出元素时能获取哪些信息"。

代码应用:

这段代码是用来解决 "将所有元素变为 0 的最少操作次数" 问题的,其核心思路是利用单调递增栈来统计需要的最少操作次数,与题目中 "每次操作可选择一个连续子数组,将其中最小的非负整数设为 0" 的规则相契合。

代码逐行解释

  1. vector<int> s; 定义一个单调递增栈,栈中存储的是 "当前需要处理的最小非负整数的递增序列"。

  2. **int res = 0;**用于记录最少操作次数。

  3. **for(int a : nums)**遍历数组中的每个元素。

  4. **while(!s.empty() && s.back() > a)**这是单调栈的核心操作:

    • 当栈不为空,且 ** 栈顶元素大于当前元素a** 时,弹出栈顶元素。
    • 目的是维护栈的单调递增性,确保栈中只保留 "当前未被覆盖的最小非负整数"。
  5. **if( a==0) continue;**如果当前元素是 0,无需处理(因为目标就是将所有元素变为 0,0 本身已经满足),直接跳过。

  6. if(s.empty() || s.back() < a) 当栈为空,或 ** 栈顶元素小于当前元素a** 时:

    • ++res;:操作次数加 1(因为需要对当前元素a执行一次新的操作)。
    • s.push_back(a);:将当前元素a入栈,维护单调递增性。

逻辑本质

代码的核心是按 "最小非负整数的递增顺序" 统计操作次数

  • 每次遇到一个新的 "未被更小元素覆盖的非 0 元素" 时,就需要一次操作来将其变为 0。
  • 单调栈确保了 "只有比栈中所有元素更大的非 0 元素" 才会触发新的操作,从而保证操作次数最少。

示例验证(以nums = [1,2,1,2,1,2]为例)

遍历过程:

  • a=1:栈空,res=1,栈变为[1]
  • a=2:栈顶1 < 2res=2,栈变为[1,2]
  • a=1:栈顶2 > 1,弹出2;栈顶1 == 1,不满足stack.back() < a,不操作,栈仍为[1]
  • a=2:栈顶1 < 2res=3,栈变为[1,2]
  • a=1:栈顶2 > 1,弹出2;栈顶1 == 1,不操作,栈仍为[1]
  • a=2:栈顶1 < 2res=4,栈变为[1,2]

最终res=4,与预期结果一致。

总结

这段代码通过单调递增栈 巧妙地维护了 "最小非负整数的递增序列",确保每次操作都针对 "当前未被覆盖的最小非 0 元素",从而得到最少操作次数,时间复杂度为O(n)(每个元素最多入栈和出栈一次)。

相关推荐
hoiii1872 小时前
基于交替方向乘子法(ADMM)的RPCA MATLAB实现
人工智能·算法·matlab
fengfuyao9852 小时前
MATLAB的加权K-means(Warp-KMeans)聚类算法
算法·matlab·kmeans
循环过三天3 小时前
3.1、Python-列表
python·算法
dragoooon344 小时前
[优选算法专题六.模拟 ——NO.40~41 外观数列、数青蛙]
数据结构·算法·leetcode
徐新帅4 小时前
CCF-GESP 等级考试 2025年3月认证C++一级真题解析
算法
陌路204 小时前
S16 排序算法--堆排序
算法·排序算法
烛衔溟4 小时前
C语言算法:排序算法入门
c语言·算法·排序算法·插入排序·冒泡排序·选择排序·多关键字排序
一匹电信狗4 小时前
【C++】封装红黑树实现map和set容器(详解)
服务器·c++·算法·leetcode·小程序·stl·visual studio
.小小陈.4 小时前
数据结构5:二叉树
数据结构