算法数据结构--单调栈

文章目录

    • 介绍
      • 单调递增栈
      • 单调递减栈
      • 图示
      • 应用场景
    • 步骤
    • 模板
    • 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 为输入序列的长度。这是因为在大多数情况下,每个元素最多进栈一次、出栈一次,所以整个过程的时间复杂度是线性的。

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

已经到底啦!!

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

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

相关文章

快讯! MySQL 8.4.0 LTS 发布(MySQL 第一个长期支持版本)

MySQL 第一个长期支持版本 8.4.0 LTS 发布&#xff0c;社区版下载地址&#xff1a; https://dev.mysql.com/downloads/mysql/ 功能变更 添加或更改的功能 组复制&#xff1a;与组复制相关的两个服务器系统变量的默认值已更改&#xff1a; 系统变量的默认值为 group_replication…

TS学习-泛型基础

目录 1&#xff0c;介绍1&#xff0c;在函数中使用2&#xff0c;在类型别名&#xff0c;接口中使用3&#xff0c;在类中使用 2&#xff0c;泛型约束3&#xff0c;多泛型4&#xff0c;举例实现 Map 1&#xff0c;介绍 泛型相当于是一个类型变量&#xff0c;有时无法预先知道具体…

JavaWeb--1.Servlet

Servlet&#xff08;基础&#xff09; 1、配置依赖&#xff1a; ​ 在pom.xml文件中加入相关依赖 <dependencies><dependency><groupId>jakarta.servlet</groupId><artifactId>jakarta.servlet-api</artifactId><version>5.0.0&l…

数据结构--栈与队列【您的关注是我创作的动力!】

文章目录 栈什么是栈&#xff1f;栈的具体实现 队列什么是队列&#xff1f;队列的实现 栈 什么是栈&#xff1f; 栈也是顺序表的一种&#xff0c;栈的逻辑实现是先进后出&#xff08;后进先出&#xff09;就跟子弹夹一样。 具体逻辑就是它只允许在固定的一端进行数据的插入与…

eNSP-抓包解析HTTP、FTP、DNS协议

一、环境搭建 1.http服务器搭建 2.FTP服务器搭建 3.DNS服务器搭建 二、抓包 三、http协议 1.HTTP协议&#xff0c;建立在FTP协议之上 2.http请求 3.http响应 请求响应报文参考&#xff1a;https://it-chengzi.blog.csdn.net/article/details/113809803 4.浏览器开发者工具抓包…

C++ 函数 参数与返回值

#一 参数与返回值 回顾文件读数据功能 文件读数据 1函数参数传值调用过程 将函数调用语句中的实参的一份副本传给函数的型材。 简单的值的传递&#xff0c;实参的值没有发生变化。 2 函数参数传值调用过程 传地址调用 将变量的地址传递给函数的形参 形参和实参指向了同…

webpack与vite

webpack 使用步骤&#xff1a; 初始化项目 pnpm init -y安装依赖webpack、webpack-cli在项目中创建src目录&#xff0c;然后编写代码&#xff08;index.js&#xff09;执行pnpm weboack来对代码进行打包&#xff08;打包后观察dist文件夹&#xff09; 配置古文件&#xff08;w…

数智新重庆 | 推进信号升格 打造算力山城

2024年&#xff0c;是实现“十四五”规划目标任务的关键一年&#xff0c;高质量的5G网络、强大的AI能力作为新质生产力的重要组成部分&#xff0c;将有效赋能包括制造业在内的千行万业数字化化、智能化、绿色化转型升级&#xff0c;推动融合应用新业态、新模式蓬勃兴起&#xf…

字符函数与字符串函数(2)

遇见她如春水映莲花 字符函数与字符串函数&#xff08;2&#xff09; 前言一、strcatstrncat 二、strcmpstrncmp在这里插入图片描述 三、strstr四、strtok五、strerror总结 前言 根据上期字符函数与字符串函数我们可以了解到字符函数与个别字符串函数的用法&#xff0c; 那么接…

SQL注入less-1

一、启动SQL注入的靶场 1、启动phpstudy 注&#xff1a;phpstudy默认的php版本为7.x&#xff0c;想要成功的搭建靶场&#xff0c;需要把php版本改为5.x 2、输入127地址加文件名 进入此界面后点击setup就行 3、进入less-1的关卡 靶场搭建成功 二、查看less-1的代码 注&#…

什么?300TB SSD要来了?

SK海力士在韩国首尔的一场新闻发布会上宣布&#xff0c;其正在研发一款前所未有的300TB容量的固态硬盘&#xff08;SSD&#xff09;。这款硬盘的预告是该公司一系列旨在推动数据中心和设备端AI能力发展的产品与技术组合的一部分。SK海力士引用市场研究预测&#xff0c;全球在AI…

每日一题(力扣740):删除并获得点数--dp+思维

其实跟打家劫舍没啥区别 排序去重之后去考虑当前位置和前两个位置之间的关系即可&#xff0c;具体见代码&#xff1a; class Solution { public:int deleteAndEarn(vector<int>& nums) {int n nums.size();if (n 1) return nums[0];unordered_map<int, int>…

代码随想录算法训练营DAY51|C++动态规划Part12|1143.最长公共子序列、1035.不相交的线、53.最大子序列和

文章目录 1143.最长公共子序列思路CPP代码 1035.不相交的线53.最大子序列和思路CPP代码 1143.最长公共子序列 力扣题目链接 文章讲解&#xff1a;1143.最长公共子序列 视频讲解&#xff1a;动态规划子序列问题经典题目 | LeetCode&#xff1a;1143.最长公共子序列 本题其实就跟…

GPU虚拟化和算力隔离探讨

1. 术语介绍 术语 全称 说明 GPU Graphics Processing Unit 显卡 CUDA Compute Unified Device Architecture 英伟达2006年推出的计算API VT/VT-x/VT-d Intel Virtualization Technology -x表示x86 CPU&#xff0c;-d表示Device SVM AMD Secure Virtual Machine …

ASP.NET视频点播系统的设计与实现

摘 要 本文阐述了基于WEB的交互式视频点播系统的协议原理、软件结构和设计实现。本视频点播系统根据流媒体传输原理&#xff0c;在校园局域网的基础上模拟基于Web的视频点播系统&#xff0c;实现用户信息管理、视频文件的添加、删除、修改及在线播放和搜索功能。本系统是一个…

6.块元素和行内元素

块元素 无论内容多少&#xff0c;该元素独占一行(p、 h1-h6…) 例子如下图 行内元素 内容撑开宽度&#xff0c;左右都是行内元素的可以排在一排(a、 strong、 em …) 例子如下 感谢您的观看&#xff0c;能和您一起学习是我最大的荣幸&#xff01; 文章学习资料&#xff1a;…

【快速入门Linux】10_Linux命令—Vi编辑器

文章目录 一、vi 简介1.1 vi1.2 vim1.3查询软连接命令&#xff08;知道&#xff09; 二、打开和新建文件&#xff08;重点&#xff09;2.1 打开文件并且定位行2.2 异常处理 三、vi三种工作模式&#xff08;重点&#xff09;3.1 末行模式-命令 四、常用命令4.0 命令线路图4.1 移…

url对象---了解url的结构

什么是url url是网页网页的地址&#xff0c;通过一个url我们可以访问到网页&#xff0c;同时url也可以用来引用文件(txt,json,jpg,js,css,html)&#xff0c;所以你可以理解成url是一个指示器&#xff0c;它可以指向一个文件&#xff0c;网页&#xff0c;图片&#xff0c;或者音…

《网络安全技术 网络安全众测服务要求》

近日&#xff0c;全国网络安全标准化技术委员会发布《网络安全技术 网络安全众测服务要求》&#xff08;GB/T 43741-2024&#xff0c;以下简称“众测服务要求”&#xff09;&#xff0c;并将在2024年11月1日正式实施。 《众测服务要求》确立了网络安全众测服务的角色及其职责&…

【skill】移动云服务器80端口

上个月玩CentOS7&#xff0c;提到alist端口问题&#xff0c;https://www.bilibili.com/read/cv33662501/ 云服务器不能被外网访问的原因 1 云服务器没放行端口 2 防火墙没放行端口 3 配置了端口转发 4 浏览器不支持搭建的网页 5 端口被其他软件占用 移动10086云服务的80端口…