单调栈题目总结

单调栈

496. 下一个更大元素 I

503. 下一个更大元素 II

739. 每日温度

6227. 下一个更大元素 IV

模版归纳

「单调栈」顾名思义就是具有单调性的栈结构,一般常用于找到下一个更大的元素,即当前元素右侧第一个更大的元素

看下面一个例子:2 1 2 4 3
在这里插入图片描述

我们只看得到比我们更高的元素,所以比我们矮的元素就无关紧要

下面给出「单调栈」的模版:

int[] nextGreaterElement(int[] nums) {
    int n = nums.length;
    // 存放答案的数组
    int[] res = new int[n];
    Stack<Integer> s = new Stack<>(); 
    // 倒着往栈里放
    for (int i = n - 1; i >= 0; i--) {
        // 判定个子高矮
        while (!s.isEmpty() && s.peek() <= nums[i]) {
            // 矮个起开,反正也被挡着了
            s.pop();
        }
        // nums[i] 身后的更大元素
        res[i] = s.isEmpty() ? -1 : s.peek();
        s.push(nums[i]);
    }
    return res;
}

现在给几个变形

如果我们现在需要找到下一个大于等于的元素,怎么办?很简单只需要修改一个地方即可:

// 注意:我们把 s.peek() == nums[i] 的元素留下了
while (!s.isEmpty() && s.peek() < nums[i]) {
    // 矮个起开,反正也被挡着了
    s.pop();
}

如果我们现在需要找到下一个更小的元素,怎么办?很简单只需要修改一个地方即可:

while (!s.isEmpty() && s.peek() >= nums[i]) {
    // 高个起开,太碍眼了
    s.pop();
}

如果我们现在需要找到上一个更大的元素,怎么办?很简单只需要变换一下遍历顺序即可:

// 正着往栈里放
for (int i = 0; i < n; i++) {
    // 其他逻辑不变
}

题目实战

上面给出了「单调栈」的模版以及几种不同的变形,下面来几道题目练练手!!

下一个更大元素 I

题目详情可见 下一个更大元素 I

这个题目基本上就是一个模版题,处理上用到了一个小技巧

由于nums1nums2的子集,所以我们先求出nums2的所有下一个更大元素,用Map映射保存一下即可

public int[] nextGreaterElement(int[] nums1, int[] nums2) {
    Map<Integer, Integer> greaterMap = new HashMap<>();
    Stack<Integer> stack = new Stack<>();
    for (int i = nums2.length - 1; i >= 0; i--) {
        while (!stack.isEmpty() && stack.peek() <= nums2[i]) stack.pop();
        // 保存 nums2[i] 下一个更大元素
        greaterMap.put(nums2[i], stack.isEmpty() ? -1 : stack.peek());
        stack.push(nums2[i]);
    }
    int[] res = new int[nums1.length];
    // 寻找 nums1 中元素在 nums2 中的下一个更大元素
    for (int i = 0; i < nums1.length; i++) {
        res[i] = greaterMap.get(nums1[i]);
    }
    return res;
}
下一个更大元素 II

题目详情可见 下一个更大元素 II

这个问题加入了一个循环数组的概念,我们处理的方法就是利用 2 倍的数组即可

为了节约空间,我们可以利用「模运算」实现 2 倍数组的效果,而不需要单独开辟空间

public int[] nextGreaterElements(int[] nums) {
    int n = nums.length;
    int[] res = new int[n];
    Stack<Integer> stack = new Stack<>();
    for (int i = 2 * n - 1; i >= 0; i--) {
        while (!stack.isEmpty() && stack.peek() <= nums[i % n]) stack.pop();
        res[i % n] = stack.isEmpty() ? -1 : stack.peek();
        stack.push(nums[i % n]);
    }
    return res;
}
每日温度

题目详情可见 每日温度

这个问题需要让我们求与下一个更大元素的间隔,所以我们稍微转变一下栈中存储的内容即可

模版中存储的内容为num[i],这样就损失了下一个更大元素的位置信息。如果我们改为存储「下标」,既保留了位置信息,也可以通过下标获得值

public int[] dailyTemperatures(int[] temperatures) {
    int[] res = new int[temperatures.length];
    Stack<Integer> stack = new Stack<>();
    for (int i = temperatures.length - 1; i >= 0; i--) {
        while (!stack.isEmpty() && temperatures[stack.peek()] <= temperatures[i]) stack.pop();
        res[i] = stack.isEmpty() ? 0 : stack.peek() - i;
        stack.push(i);
    }
    return res;
}
下一个更大元素 IV

题目详情可见 下一个更大元素 IV

这个题目更形象的名字应该是:下下个更大的元素

public int[] secondGreaterElement(int[] nums) {
    int n = nums.length;
    int[] ans = new int[n];
    Arrays.fill(ans, -1);
    // s1 用来存储还没有遇到「比它大的下一个元素」的元素
    Deque<Integer> s1 = new ArrayDeque<>();
    // s2 用来存储遇到一个「比它大的下一个元素」的元素
    Deque<Integer> s2 = new ArrayDeque<>();
    for (int i = 0; i < nums.length; i++) {
        // 如果 s2 中有比 nums[i] 小的元素,说明 nums[i] 就是该元素的下下个更大的元素
        while (!s2.isEmpty() && nums[i] > nums[s2.peek()]) ans[s2.poll()] = nums[i];
        Deque<Integer> t = new ArrayDeque<>();
        // 如果 s1 中有比 nums[i] 小的元素,说明该元素遇到了一个「比它大的下一个元素」的元素,移到 s2 中
        // 注意:需要按顺序移动!!比如在 s1 中的顺序是 a1 a2 a3,那么移到 s2 中的存储顺序也应该 a1 a2 a3
        // 所以这里借助另外一个栈来中转一下,不然顺序会反转
        while (!s1.isEmpty() && nums[i] > nums[s1.peek()]) t.push(s1.poll());
        s1.push(i);
        while (!t.isEmpty()) s2.push(t.poll());
    }
    return ans;
}

其实除了用两个栈,还可以用两个堆来实现,堆比栈更好理解,但是原理都是一样的!

public int[] secondGreaterElement(int[] nums) {
    int n = nums.length;
    int[] ans = new int[n];
    Arrays.fill(ans, -1);
    // 小根堆
    Queue<Integer> q1 = new PriorityQueue<>((a, b) -> nums[a] - nums[b]);
    Queue<Integer> q2 = new PriorityQueue<>((a, b) -> nums[a] - nums[b]);
    for (int i = 0; i < n; i++) {
        int x = nums[i];
        while (!q2.isEmpty() && nums[q2.peek()] < x) ans[q2.poll()] = x;
        while (!q1.isEmpty() && nums[q1.peek()] < x) q2.offer(q1.poll());
        q1.offer(i); 
    }
    return ans;
}

引用

  • https://lfool.github.io/LFool-Notes/algorithm/%E5%8D%95%E8%B0%83%E6%A0%88.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/393952.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

消毒柜行业分析:市场渗透率不足20%

目前消毒柜仍然属于“小众”品类&#xff0c;疫情前期市场渗透率也不足20%。有业内人士表示&#xff0c;多年来消毒柜零售量规模基本在400万台左右徘徊&#xff0c;这个角度看&#xff0c;消毒柜是具有自身的产品消费人群的&#xff0c;其市场相对稳定&#xff0c;而且消毒柜的…

DoRA(权重分解低秩适应):一种新颖的模型微调方法

来自&#xff1a;小互 DoRA&#xff08;权重分解低秩适应&#xff09;&#xff1a;一种新颖的模型微调方法 DoRA在LoRA的基础上进一步发展&#xff0c;通过将预训练权重分解为“幅度”和“方向”两个部分进行微调。 这种权重分解方法允许DoRA更精细地控制模型的学习过程&…

error: ‘QWidget‘ file not found

说明你没有加载 widgets模块 缺少widgets&#xff0c;就报错

mysql 2-17

UNION关键字和UNION ALL 自然连接 USING使用 函数 单行函数 基本函数 三角函数 指数和对数 进制间的转换 字符串函数 时间和日期函数 计算日期和时间的函数 日期的格式化和解析 流程控制函数

这样用TVS管

对于工程师来说&#xff0c;浪涌保护不仅仅是选择合适的电源板或者拔下几根电缆&#xff0c;主要涉及在 PCB 布局中放置瞬态保护组件并应用明确的接地策略。 TVS 二极管是用于保护PCB布局中组件的常用组件&#xff0c;这些组件放置在数据线上&#xff0c;一旦电路中接收到ESD脉…

激活函数30年回顾总结,全paper第一份详尽研究来了!

B站&#xff1a;啥都会一点的研究生公众号&#xff1a;啥都会一点的研究生 新年好&#xff0c;离退休又近了一年 假期躺平未更新&#xff0c;但该保存的素材及热点还是拿小本本记了下来&#xff0c;如这篇今年2月14号arXiv上发表的长达100页神经网络中激活函数大总结文章就进…

综合练习

目录 查询每个员工的编号、姓名、职位、基本工资、部门名称、部门位置 确定要使用的数据表 确定已知的关联字段 查询每个员工的编号、姓名、职位、基本工资、工资等级 确定要使用的数据表 确定已知的关联字段 查询每个员工的编号、姓名、职位、基本工资、部门名称、工资…

string的用法

概念 可代替字符数组来存储字符串 访问 string name[i];//下标访问 string::iterator it;//迭代器访问常用函数 1.begin():获得字符串首地址 2.end():获得字符串末地址 3.&#xff1a;字符串的加法&#xff0c;可将两个字符串拼接起来 4.比较符&#xff1a;&#xff0c;>…

GET与 POST

资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) GET 和 POST 有什么区别? 根据 REC 规范&#xff0c;GET的语义是从服务器获取指定的资源&#xff0c;这个资源可以是静态的文本、页面、图片视频等。GET请求的参数位置一般是写在 URL 中&#xff0c;UR…

Python Selenium实现自动化测试及Chrome驱动使用!

本文将介绍如何使用Python Selenium库实现自动化测试&#xff0c;并详细记录了Chrome驱动的使用方法。 通过本文的指导&#xff0c;读者将能够快速上手使用Python Selenium进行自动化测试。 并了解如何配置和使用Chrome驱动来实现更高效的自动化测试。 一、Python Selenium简…

ClickHouse监控及备份

第1章 ClickHouse监控概述 第2章 Prometheus&Grafana的安装 第3章 ClickHouse配置 第4章 Grafana集成Prometheus 第5章 备份及恢复

2024 前端面试题(GPT回答 + 示例代码 + 解释)No.114 - No.121

本文题目来源于全网收集&#xff0c;答案来源于 ChatGPT 和 博主&#xff08;的小部分……&#xff09; 格式&#xff1a;题目 h3 回答 text 参考大佬博客补充 text 示例代码 code 解释 quote 补充 quote 上一篇链接&#xff1a;2024 前端面试题&#xff08;GPT回答 示例…

Linux系统:iptables 防火墙

目录 一、安全技术与防火墙 1、安全技术概念 2、防火墙 2.1 防火墙概念 2.2 防火墙分类 2.3 linux的防火墙Netfilter 2.4 防火墙工具介绍 2.5 netfilter 和 iptables 的关系 二、iptables 1、概念 2、五表五链 2.1 五个table表 2.2 五个chain链 2.3 内核中数据包…

全网最详细的从0到1的turbo pnpm monorepo的前端工程化项目[搭建篇]

全网最详细的从0到1的turbo pnpm monorepo的前端工程化项目[搭建篇] 引言相关环境技术栈初始化工程安装turbo配置pnpm-workspace安装husky安装lint-staged安装eslint安装prettier配置 .editorconfig配置 .gitignore初步项目结构结语 引言 最近各种原因&#xff0c;生活上的&am…

PHP支持的伪协议

php.ini参数设置 在php.ini里有两个重要的参数allow_url_fopen、allow_url_include。 allow_url_fopen:默认值是ON。允许url里的封装协议访问文件&#xff1b; allow_url_include:默认值是OFF。不允许包含url里的封装协议包含文件&#xff1b; 各协议的利用条件和方法 php:/…

机器人内部传感器-位置传感器-电位器式位置传感器

位置传感器 位置感觉是机器人最基本的感觉要求&#xff0c;可以通过多种传感器来实现。位置传感器包括位置和角度检测传感器。常用的机器人位置传感器有电位器式、光电式、电感式、电容式、霍尔元件式、磁栅式及机械式位置传感器等。机器人各关节和连杆的运动定位精度要求、重…

【Java】图解 JVM 垃圾回收(一):GC 判断策略、引用类型、垃圾回收算法

图解 JVM 垃圾回收&#xff08;一&#xff09; 1.前言1.1 什么是垃圾1.2 内存溢出和内存泄漏 2.垃圾回收的定义与重要性3.GC 判断策略3.1 引用计数算法3.2 可达性分析算法 4.引用类型5.垃圾回收算法5.1 标记-复制&#xff08;Copying&#xff09;5.2 标记-清除&#xff08;Mark…

Diffusion Model——扩散模型

Diffusion Model 文章目录 Diffusion ModelDenoising Diffusion Probabilistic Model(DDPM)去噪过程&#xff1a;Denoise结构训练过程Text-to-image Generation Model High Resolution Image Synthesis With_Latent Diffusion Models (Stable Diffusion)基本结构与推理过程Text…

【OpenAI Sora】 最强文生视频怎么用-新手小白必看教程

1. Sora 是什么AI 2024年2月16日&#xff0c;OpenAI在其官网上面正式宣布推出文本生成视频的大模型 Sora。 Sora能够根据简单的文本描述&#xff0c;生成高达60秒的高质量视频&#xff0c;使得视频创作变得前所未有的简单和高效。本文将为您提供关于如何使用Sora的最新详细教程…

Days 34 ElfBoard 音频接口

音频接口介绍 音频模块采用了 NAU88C22 芯片&#xff0c;芯片数据信号使用 I2S 接口进行通讯&#xff0c;主要信号功能&#xff1a; SAI_MCLK&#xff1a;音频信号主时钟&#xff1b; SAI_BCLK&#xff1a;音频信号位时钟&#xff1b; SAI_SYNC&#xff1a;左右声道控制信号&am…