算法数据结构--单调栈

news2025/1/13 13:23:05

文章目录

    • 介绍
      • 单调递增栈
      • 单调递减栈
      • 图示
      • 应用场景
    • 步骤
    • 模板
    • Deque用法
    • 例题
      • [739. 每日温度](https://leetcode.cn/problems/daily-temperatures/)
      • [496. 下一个更大元素 I](https://leetcode.cn/problems/next-greater-element-i/)
    • 总结

介绍

单调栈是一种特殊的栈数据结构,其特点在于栈内的元素保持单调性。单调栈通常分为单调递增栈和单调递减栈两种类型。

单调递增栈

​ 单调递增栈中的元素从栈底到栈顶依次递增。当我们向单调递增栈中压入一个元素时,如果该元素比栈顶元素大,就直接入栈;如果该元素比栈顶元素小,则不断将栈顶元素出栈,直到栈为空或者栈顶元素小于等于当前元素,然后再将当前元素入栈。单调递增栈主要用于解决一些需要找到右边第一个较大元素的问题。

单调递减栈

​ 单调递减栈中的元素从栈底到栈顶依次递减。当我们向单调递减栈中压入一个元素时,如果该元素比栈顶元素小,就直接入栈;如果该元素比栈顶元素大,则不断将栈顶元素出栈,直到栈为空或者栈顶元素大于等于当前元素,然后再将当前元素入栈。单调递减栈主要用于解决一些需要找到右边第一个较小元素的问题。

图示

在这里插入图片描述

应用场景

单调栈在解决一些数组或序列相关问题时非常有效。

例如:

  • 寻找数组中下一个较大或较小元素;
  • 计算滑动窗口的最大值或最小值;
  • 解决一些与序列相关的动态规划问题;
  • 解决一些需要找到数组中局部极值或单调性的问题等。

步骤

单调栈的步骤一般分为:

  1. 定义一个栈

    Deque<Integer> deque=new ArrayDeque<Integer>();
    
  2. 确定栈里存储的数据是什么

    一般情况下单调栈里面存放的都是目标数组的下标,这样做的好处就是,我们不仅知道遍历过的元素下标,而且也可以通过下标找到对应数组里面的元素。

  3. 确定使用单调递增栈还是单调递减栈

    如果求一个元素右边第一个更大元素时,单调栈就是递增的,如果求一个元素右边第一个更小元素,单调栈就是递减的。

  4. 遍历目标数组,比较当前遍历的元素与栈顶元素,并进行处理
    这里在遍历目标数组的时候,需要比较当前遍历元素和栈顶元素的大小。
    这个时候就有如下三种情况:

    • 当前遍历的元素小于栈顶元素的情况
    • 当前遍历的元素等于栈顶元素的情况
    • 当前遍历的元素大于栈顶元素的情况

    一般情况下单调递增栈在当前遍历的元素大于栈顶元素的情况下进行主要的逻辑处理,其他情况下直接把当前元素做入栈操作;而单调递减栈在当前遍历的元素小于栈顶元素的情况下进行主要的逻辑处理,其他情况下直接把当前元素做入栈操作。

    注意:在对栈进行操作的时候,要尽量减少对栈的操作次数。比如,如果在获取栈顶元素后,还要进行弹出操作且再获取栈顶元素后不需要在将该元素保留栈顶。则此时,可以直接使用pop()方法,代替使用peek()方法后再使用pop()方法。

  5. 返回结果

模板

import java.util.*;

public class MonotonicStackSolver {
    
    // 单调递增栈
    public void solveIncreasing(int[] nums) {
        Deque<Integer> stack = new ArrayDeque<>();
        // 遍历数组
        for (int i = 0; i < nums.length; i++) {
            // 维护单调性
            while (!stack.isEmpty() && nums[i] < stack.peek()) {
                stack.pop();
                // 在这里可以执行相关操作
            }
            // 入栈
            stack.push(nums[i]);
        }
    }

    // 单调递减栈
    public void solveDecreasing(int[] nums) {
        Deque<Integer> stack = new ArrayDeque<>();
        // 遍历数组
        for (int i = 0; i < nums.length; i++) {
            // 维护单调性
            while (!stack.isEmpty() && nums[i] > stack.peek()) {
                stack.pop();
                // 在这里可以执行相关操作
            }
            // 入栈
            stack.push(nums[i]);
        }
    }

    public static void main(String[] args) {
        MonotonicStackSolver solver = new MonotonicStackSolver();
        
        // 示例用法
        int[] nums = {2, 5, 9, 3, 1, 12, 6, 8};
        solver.solveIncreasing(nums); // 使用单调递增栈解决问题
        // 或者
        solver.solveDecreasing(nums); // 使用单调递减栈解决问题
    }
}

Deque用法

我们在用Java写单调栈题目的时候一般需要用到以下这几个方法:

  • deque.isEmpty():如果deque不包含任何元素,则返回true,否则返回false。因为要栈顶元素在满足要求的时候要弹出,所以需要进行空栈判断。有些场景,可能栈一定不会空的时候,就不需要该方法进行空栈判断。
  • deque.push(e):将元素e入栈。
  • deque.pop():将栈顶元素弹出,并返回当前弹出的栈顶元素。
  • deque.peek():获取栈顶元素,不弹出。

例题

下面我们用两个简单的例题来加深我们对单调栈的理解:

739. 每日温度

在这里插入图片描述

本题意思就是让我们去找每一个元素下一个比他更大的元素出现在后面的哪里,如果有则记录下标之差,如果没有则设置为0,最后返回答案数组。

我们看下代码:

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int[] ans = new int[temperatures.length];//定义答案数组
        Deque<Integer> stack = new ArrayDeque<Integer>();//定义一个栈
        for (int i = 0; i < temperatures.length; i++) {
            while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {//如果栈不为空且遍历的元素大于栈顶元素,那么就要出栈,并且此时也证明栈顶元素找到了下一个比它更大的元素的位置,记录下标之差就可以了
                int m = stack.pop();//弹出栈顶元素(下标)
                ans[m] = i - m;//用此时遍历的元素下标减去栈顶元素下标就是栈顶元素下标下一个比他温度更高的天数
            }
            stack.push(i);//其他情况(栈为空或者遍历元素小于等于栈顶元素)都入栈
        }
        return ans;//返回答案
    }
}

我们往栈内存储的元素是下标而不是元素数值,这样我们就可以很容易地求出下标之差。

用Deque比自己创建一个数组来表示栈更方便一点

496. 下一个更大元素 I

在这里插入图片描述

本题就是让我们去找在nums1中出现过的数字在nums2中去找比它大的第一个数字是多少,答案要存储在nums1中的对应下标位置。

本题我们很容易就可以联想到要使用哈希表去存储nums1中出现过的数字,当循环nums2数组的时候就可以直接判断存在不存在了。

下面我们直接来看代码:

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int[] hash = new int[10001];
        for (int i = 0; i < nums1.length; i++) {
            hash[nums1[i]] = i + 1;//这里可以将哈希表对应下标位置的数值直接设置成i+1,这样在之后记录答案的时候直接在答案数组hash[]-1的位置记录就可以了
        }
        Deque<Integer> stack = new ArrayDeque<Integer>();
        int[] ans = new int[nums1.length];
        int ansTop = 0;
        for (int i = 0; i < nums1.length; i++)
            ans[i] = -1;
        for (int i = 0; i < nums2.length; i++) {
            while(!stack.isEmpty()&&nums2[i]>nums2[stack.peek()]){
                int m = stack.pop();
                ans[hash[nums2[m]]-1] = nums2[i];
            }
            if(hash[nums2[i]]!=0)//遍历的数字在nums1中出现过才能入栈
                stack.push(i);
        }
        while(!stack.isEmpty()){
            int m = stack.pop();
            ans[hash[nums2[m]]-1] = -1;//栈内剩余的部分就是后面没有比他大的数字,直接设置成-1就可以了
        }
        return ans;
    }
}

总结

总的来说,单调栈是一种高效、简洁且灵活的数据结构。

单调栈的空间复杂度通常很低,因为它只需要存储输入序列中的一部分元素,而不需要额外的空间。

通常情况下,单调栈的时间复杂度为 O(n),其中 n 为输入序列的长度。这是因为在大多数情况下,每个元素最多进栈一次、出栈一次,所以整个过程的时间复杂度是线性的。

所以单调栈在解决数组或序列相关问题时具有很好的效果。

已经到底啦!!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Excel中怎样把单元格里的数据拆分成多行?

时常会遇到这种情况&#xff0c;需要将一个单元格里的数据分拆到多行&#xff0c;可以使用公式&#xff0c;这里演示使用基础操作的办法。 按照excel使用经验&#xff0c;可以复制数据&#xff0c;粘贴到MS Word里&#xff0c;这个是excel的同族软件&#xff0c;兼容性好。 在…

基于SSM SpringBoot vue教务排课系统

基于SSM SpringBoot vue教务排课系统 系统功能 登录 个人中心 学生信息管理 教师信息管理 课室信息管理 班级信息管理 系别信息管理 专业信息管理 课程信息管理 选课信息管理 课表信息管理 开发环境和技术 开发语言&#xff1a;Java 使用框架: SSM(Spring SpringMVC Myba…

基于 AI 的数据库助手-Chat2DB

序言 现在已经开始步入 AI 时代&#xff0c;AI 产品也已经络绎不绝。今天&#xff0c;给大家介绍一款数据库的 AI 产品 —— Chat2DB。 一、什么是 Chat2DB Chat2DB 由阿里提供的一个数据库管理、数据开发、数据分析的工具&#xff0c;它是一个 AI 原生的数据库管理工具&…

STM32数字示波器+详细注释+上位机程序+硬件

目录 1、设计指标&#xff1a; 2、功能&#xff1a; 3、上位机的程序 ​4、测试的照片 5、PCB 6、模拟电路板 7、程序 资料下载地址&#xff1a;STM32数字示波器详细注释上位机程序硬件 1、设计指标&#xff1a; 主控: STM32…

2022 亚马逊云科技中国峰会,对话开发者论坛

目录 前言 最近整理资料发现还有一些前 2 年的内容没发出来&#xff0c;故补发记录&#xff0c;每年都有新的感悟。 开发者论坛 1. 你认为什么是开发者社区&#xff0c;如何定义一个成功的开发者社区&#xff1f; 我认为可以把开发者社区看成一个 “产品” 来对待&#xff…

大功率双向直流电源的输出电压和电流

双向直流电源&#xff08;Bidirectional DC Power Supply&#xff09;是一种具有双向输出电能的直流电源。与传统的直流电源相比&#xff0c;双向直流电源在输出电能的同时&#xff0c;还具备一定的电流输入能力&#xff0c;从而使其应用范围更加广泛。大功率双向直流电源作为电…

n-Track Studio Suite for Mac激活版:打造您的专属音频工作室

n-Track Studio Suite for Mac是一款功能强大的数字音频工作站软件&#xff0c;让您在家中就能享受到专业录音棚的待遇。无论是录制人声、乐器还是MIDI序列&#xff0c;都能轻松应对。 n-Track Studio Suite for Mac激活版下载 这款软件拥有实时音高校准、时间拉伸和自动补足功…

Java项目:基于SSM框架实现的学院党员管理系统高校党员管理系统(ssm+B/S架构+源码+数据库+毕业论文+开题)

一、项目简介 本项目是一套基于SSM框架实现的学院党员管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、功能齐…

机器学习:深入解析SVM的核心概念【一、间隔与支持向量】

直接阅读原始论文可能有点难和复杂&#xff0c;所以导师直接推荐我阅读周志华的《西瓜书》&#xff01;&#xff01;然后仔细阅读其中的第六章&#xff1a;支持向量机 间隔与支持向量 **问题一&#xff1a;什么叫法向量&#xff1f;为什么是叫法向量**什么是法向量&#xff1f;…

OpenCV如何模板匹配(59)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV如何实现背投(58) 下一篇 &#xff1a;OpenCV在图像中寻找轮廓(60) 目标 在本教程中&#xff0c;您将学习如何&#xff1a; 使用 OpenCV 函数 matchTemplate()搜索图像贴片和输入…

C语言 | Leetcode C语言题解之第60题排列序列

题目&#xff1a; 题解&#xff1a; char* getPermutation(int n, int k) {int factorial[n];factorial[0] 1;for (int i 1; i < n; i) {factorial[i] factorial[i - 1] * i;}--k;char* ans malloc(n 1);ans[n] \0;int valid[n 1];for (int i 0; i < n; i) {val…

OpenWRT部署Zerotier虚拟局域网实现内网穿透

前言 细心的小伙伴肯定已经发现了&#xff1a;电脑上部署了Zerotier&#xff0c;如果路由器也部署了OpenWRT&#xff0c;那是否能远程访问呢&#xff1f; 答案是肯定的。 OpenWRT部署Zerotier有啥好处&#xff1f; 那好处必须多&#xff0c;其中的一个便是在外远程控制家里…

【UnityRPG游戏制作】NPC交互逻辑、动玩法

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;就业…

Oracle 数据库全面升级为 23ai

从 11g 到 12c 再到 19c&#xff0c;今天&#xff0c;我们迎来了 23ai &#xff01; “ Oracle AI Vector Search allows documents, images, and relational data that are stored in mission-critical databases to be easily searched based on their conceptual content Ge…

算法打卡day40

今日任务&#xff1a; 1&#xff09;139.单词拆分 2&#xff09;多重背包理论基础&#xff08;卡码网56携带矿石资源&#xff09; 3&#xff09;背包问题总结 4&#xff09;复习day15 139单词拆分 题目链接&#xff1a;139. 单词拆分 - 力扣&#xff08;LeetCode&#xff09; …

【22-处理不平衡数据集:Scikit-learn中的技术和策略】

文章目录 前言了解不平衡数据集重采样技术过采样欠采样生成合成样本调整类别权重使用适合于不平衡数据集的评估指标结论前言 在机器学习任务中,不平衡数据集是一个非常常见的问题。它指的是数据集中各类别样本数量差异较大,这种情况在现实世界的数据收集中非常普遍,特别是在…

WebDriver使用带用户名密码验证的IP代理解决方案

背景&#xff0c;使用python3 selenium 先定义一个方法&#xff0c;这里主要用到了chrome插件的功能&#xff0c;利用这个插件来放进代理内容。 def create_proxy_auth_extension(proxy_host, proxy_port,proxy_username, proxy_password, schemehttp):manifest_json "…

专业渗透测试 Phpsploit-Framework(PSF)框架软件小白入门教程(一)

本系列课程&#xff0c;将重点讲解Phpsploit-Framework框架软件的基础使用&#xff01; 本文章仅提供学习&#xff0c;切勿将其用于不法手段&#xff01; Phpsploit-Framework&#xff08;简称 PSF&#xff09;框架软件&#xff0c;是一款什么样的软件呢&#xff1f; Phpspl…

开源的贴吧数据查询工具

贴吧数据查询工具 这是一个贴吧数据查询工具&#xff0c;目前仍处于开发阶段。 本地运行 要本地部署这个项目&#xff0c;请 克隆这个仓库并前往项目目录 git clone https://github.com/Dilettante258/tieba-tools.git cd tieba-tools安装依赖 pnpm install运行项目 np…

如何配置Jupyter Lab以允许远程访问和设置密码保护

如何配置Jupyter Lab以允许远程访问和设置密码保护 当陪你的人要下车时&#xff0c;即使不舍&#xff0c;也该心存感激&#xff0c;然后挥手道别。——宫崎骏《千与千寻》 在数据科学和机器学习工作流中&#xff0c;Jupyter Lab是一个不可或缺的工具&#xff0c;但是默认情况下…