算法之单调栈常见题目

news2024/9/30 9:25:06

什么时候需要使用单调栈?
通常是一维数组,要寻找任意一个右边或者左边第一个比自己大或小的元素的位置,此时我们就想到可以使用单调栈了
单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是整个数组只需要遍历一次。所以时间复杂度是O(n)就可以找到每一个元素的右边第一个比它大的元素位置呢?
更直白来说,就是用一个栈来记录我们遍历过的元素,因为我们遍历数组的时候,我们不知道之前都遍历了哪些元素,以至于遍历一个元素找不到是不是之前遍历过一个更小的,所以我们需要用一个容器(这里用单调栈)来记录我们遍历过的元素。
leedcode739. 每日温度
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。

示例 1:

输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
示例2:
输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]
示例 3:

输入: temperatures = [30,60,90]
输出: [1,1,0]

在这里插入图片描述

class Solution {
  //单调栈就是解决找到第一个比原来的数大或小的数,然后进行出栈并通过相减求出之前的坐标
    public int[] dailyTemperatures(int[] temperatures) {

        //创建一个栈,存储单调递增,存的是下标
        Stack<Integer> stack=new Stack<>();
        //第一个存储的元素默认是第一个数组元素下标
        stack.push(0);
        //存放结果的result数组
        int[] result=new int[temperatures.length];
        //默认都是0,因为后面可以没有再比他大的数那么温度不会再升高,就是0
        Arrays.fill(result,0);
        for(int i=1;i<temperatures.length;i++){

            if(temperatures[i]<=temperatures[stack.peek()]){
                stack.push(i);
            }
            while(!stack.isEmpty()&&temperatures[i]>temperatures[stack.peek()]){//比较到最后可能栈已经弹完,需要进行判空
                result[stack.peek()]=i-stack.pop();
            }
            //比较完才加入当前下标
            stack.push(i);

        }
        return result;

    }
}

leedcode496. 下一个更大元素 I
nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。

给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。

对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。

返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。

示例 1:

输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1中每个值的下一个更大元素如下所述:

  • 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
  • 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
  • 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。

示例 2:
输入:nums1 = [2,4], nums2 = [1,2,3,4].
输出:[3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:

  • 2 ,用加粗斜体标识,nums2 = [1,2,3,4]。下一个更大元素是 3 。
  • 4 ,用加粗斜体标识,nums2 = [1,2,3,4]。不存在下一个更大元素,所以答案是 -1 。

注意题目中说是两个没有重复元素 的数组 nums1 和 nums2。
没有重复元素,我们就可以用map来做映射了。根据数值快速找到下标,还可以判断nums2[i]是否在nums1中出现过。
从题目示例中我们可以看出最后是要求nums1的每个元素在nums2中下一个比当前元素大的元素,那么就要定义一个和nums1一样大小的数组result来存放结果。


    public int[] nextGreaterElement(int[] nums1, int[] nums2) {

        //建立一个单调栈
        Deque<Integer> stack = new LinkedList<>();
        Map<Integer, Integer> map = new HashMap();
        for (int i = 0; i < nums1.length; i++) {
            map.put(nums1[i], i);
        }
        //存放数组
        int[] result = new int[nums1.length];
        Arrays.fill(result, -1);
        //默认栈中第一个元素的下标是0
        stack.push(0);
        for (int i = 1; i < nums2.length; i++) {
            //情况一,栈顶元素和当前遍历元素相等
            if (nums2[i] == nums2[stack.peek()]) {
                stack.push(i);
            }
            //情况二,栈顶元素大于当前遍历元素
            if (nums2[i] < nums2[stack.peek()]) {
                stack.push(i);
            }
            //情况三,栈顶元素小于当前遍历元素
            //对栈中元素进行判空,并比较结果
            while (!stack.isEmpty() && nums2[i] > nums2[stack.peek()]) {
                //因为主要求得是num1每个值对应的下一个元素,所以判断当前map是否存在nums1中的元素
                if (map.containsKey(nums2[stack.peek()])) {
                    Integer index = map.get(nums2[stack.peek()]);
                    result[index] = nums2[i];//存放比当前nums1大的下一个元素
                }
                stack.pop();
            }
            stack.push(i);
        }
        return result;
    }

503. 下一个更大元素 II
给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。

数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。

示例 1:

输入: nums = [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2; 数字 2找不到下一个更大的数; 第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

示例 2:

输入: nums = [1,2,3,4,3]
输出: [2,3,4,-1,4]

对于成环的题目可以考虑使用取模

  //单调栈成环,一般用模%
    public int[] nextGreaterElements(int[] nums) {

        //栈
        Deque<Integer> stack=new LinkedList<>();
        int[] result=new int[nums.length];
        if(nums.length==0){
            return result;
        }
        Arrays.fill(result,-1);
        stack.push(0);
        //遍历长度为原来的两倍
        for (int i=1;i<(nums.length*2);i++){
            if(nums[i % nums.length]<nums[stack.peek()]){
                stack.push(i%nums.length);
            }
            if(nums[i%nums.length]==nums[stack.peek()]){
                stack.push(i%nums.length);//放栈下标的时候也要记得取模,不然nums[stack.peek()]可能会越界
            }
            while(!stack.isEmpty()&&nums[i%nums.length]>nums[stack.peek()]){
                result[stack.peek()]=nums[i%nums.length];
                stack.pop();
            }
            stack.push(i);
        }
        return result;
    }

leedcode42. 接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
在这里插入图片描述

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。

因为一旦发现添加的柱子高度大于栈头元素了,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。
在这里插入图片描述
在这里插入图片描述
从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。

因为一旦发现添加的柱子高度大于栈头元素了,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。
在这里插入图片描述

   public int trap(int[] height) {

        //创建单调栈,找到左右两边第一个比它大的数,单调递增栈
        Deque<Integer> stack=new LinkedList<>();
        int result=0;
        //刚开始放首个元素
        stack.push(0);
        for(int i=1;i<height.length;i++) {
        //如果当前遍历的元素(柱子)高度小于栈顶元素的高度,就把这个元素加入栈中,因为栈里本来就要保持从小到大的顺序(从栈头到栈底)。
            if (height[i] < height[stack.peek()]) {
                stack.push(i);
            }
            //如果当前遍历的元素(柱子)高度等于栈顶元素的高度,要跟更新栈顶元素,因为遇到相相同高度的柱子,需要使用最右边的柱子来计算宽度。
           if(height[i]==height[stack.peek()]){
                stack.pop();
                stack.push(i);
            }
            //如果当前遍历的元素(柱子)高度大于栈顶元素的高度,此时就出现凹槽了
            while (!stack.isEmpty()&&height[i] > height[stack.peek()]) {
                int mid = stack.pop();
                if (!stack.isEmpty()) {
                    int left = stack.peek();
                    int right = i;
                    //计算宽
                    int w = right - left - 1;
                    //计算高
                    int h = Math.min(height[left], height[right]) - height[mid];
                    result += w * h;
                }

            }
            stack.push(i);
        }
        return result;
    }

leedcode84. 柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。
在这里插入图片描述
本题和接雨水拿到题很像,只不过是找每个柱子左右两边第一个小于该柱子的柱子,所以从栈头(元素从栈头弹出)到栈底的顺序应该是从大到小的顺序!
在这里插入图片描述
只有栈里从大到小的顺序,才能保证栈顶元素找到左右两边第一个小于栈顶元素的柱子。
主要有三种情况:
情况一:当前遍历的元素heights[i]大于栈顶元素heights[stack.peek()]的情况
情况二:当前遍历的元素heights[i]等于栈顶元素heights[stack.peek()]的情况
情况三:当前遍历的元素heights[i]小于栈顶元素heights[stack.peek()]的情况
需要注意的是要在原来的数组里面首部和尾部加0
首先来说末尾为什么要加元素0?
如果数组本身就是升序的,例如[2,4,6,8],那么入栈之后 都是单调递减,一直都没有走情况三 计算结果的哪一步,所以最后输出的就是0了。 如图:
在这里插入图片描述
那么结尾加一个0,就会让栈里的所有元素,走到情况三的逻辑。

开头为什么要加元素0?
如果数组本身是降序的,例如 [8,6,4,2],在 8 入栈后,6 开始与8 进行比较,此时我们得到 mid(8),rigt(6),但是得不到 left。
在这里插入图片描述
计算过程
在这里插入图片描述

class Solution {
 public int largestRectangleArea(int[] heights) {

           // 数组扩容,在头和尾各加入一个元素
        int [] newHeights = new int[heights.length + 2];
        newHeights[0] = 0;
        newHeights[newHeights.length - 1] = 0;
        for (int index = 0; index < heights.length; index++){
            newHeights[index + 1] = heights[index];
        }

        heights = newHeights;
         int result=0;
        Deque<Integer> stack=new LinkedList<>();
        stack.push(0);
        //单调递减栈
        for(int i=1;i<heights.length;i++){
            if(heights[i]>heights[stack.peek()]){
                stack.push(i);
            }
            if(heights[i]==heights[stack.peek()]){
                stack.pop();
                stack.push(i);
            }
            while(!stack.isEmpty()&&heights[i]<heights[stack.peek()]){
                //中间元素
                int mid=stack.pop();
                if(!stack.isEmpty()){
                    //左边,右边找最大
                    int height=heights[mid];
                    int width=i-stack.peek()-1;
                    result=Math.max(result,height*width);
                }
             
            }
            stack.push(i);
        }
        return result;
    }
}

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

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

相关文章

电轨车运维作业vr实操培训平台训练一批高素质的维修型人才

卡车由于使用频繁、长期载重以及各种不可预测的外界作业技能人员的培训。基于web3d开发的卡车检修作业3d模拟仿真教学系统弥补了教学条件的不足&#xff0c;在提升培训效果、节省教学经费上有显著的作用。 深圳华锐视点研发的卡车检修作业3d模拟仿真教学系统实时动态展示三维仿…

GNN 学习记录

GNN 参考资料&#xff1a;https://www.bilibili.com/video/BV16v4y1b7x7 图网络为什么复杂 需要接受任意尺寸的输入没有固定的节点顺序和参考锚点&#xff08;比如文本是从前往后处理&#xff0c;图像是有像素点的&#xff0c;图没有起始点&#xff09;动态变化和多种模态的…

月入3000万,23岁美国女网红用AI分身交1000多男友!谈恋爱按分钟计费

来源 | 新智元 微信号&#xff1a;AI-era 【导读】这位23岁的女网红用GPT-4复刻了一个自己后&#xff0c;已经周入7万多美元了。不仅如此&#xff0c;短短几天内&#xff0c;候补名单上就排了差不多1万名男施主。 各种逼真的AI工具火了之后&#xff0c;各路心思活泛的选手都开…

Spring Boot 项目【前后端分离】 之架子搭建

Spring Boot 项目【前后端分离】 之架子搭建 注意如果看过我ssm项目的博客的项目的前端可以不需要看或者快速看一下即可 比较页面什么的一样主要是技术栈不同. 技术栈 - 使用了前后端分离&#xff0c;前端的主体框架Vue3&#xff0b;后端的基础框架Spring-Boot 1.前端技术栈…

SQL: STUFF()和FOR XML PATH的使用

STUFF(param1, startIndex, length, param2) 将param1中自startIndex(SQL中都是从1开始&#xff0c;而非0)起&#xff0c;删除length个字符&#xff0c;然后用param2替换删掉的字符。 示例&#xff1a; select STUFF(abcdefg,1,0,1234) --结果为1234abcdefg select ST…

每日一题163——矩阵置零

给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]] 示例 2&#xff1a; 输入&…

Docker与Docker-compose安装Vulfocus Vulhub漏洞环境

目录 一.docker 和 docker-compose 介绍&#xff1a; docker&#xff1a; docker-compose&#xff1a; 二者的区别&#xff1a; 二者的联系&#xff1a; 二者的总结&#xff1a; 二.Centos 7安装Docker 三.Centos 7安装docker-compose 四.docker-compose搭建Vulhub漏…

Linux安装MySQL后无法通过IP地址访问处理方法

本文主要总结Linux安装Mysql后&#xff0c;其他主机访问不了MySQL数据库的原因和解决方法 环境说明&#xff1a; MySQL 5.7.30CentOS Linux release 7.6.1810 (Core) 创建完Mysql数据库后可以查看mysql 日志获取root 用户登录密码 [rootlocalhost mysql-5.7.30]# cat /var/l…

2 机器学习知识 Softmax回归 deep learning system

机器学习算法的三个主要部分 The hypothesis class: 模型结构loss fuction 损失函数An optimization method&#xff1a;在训练集上减小loss的方法 多分类问题 训练数据&#xff1a; x ( i ) ∈ R n , y ( i ) ∈ 1 , . . . , k f o r i 1 , . . . m x^{(i)}\in \mathbb{R}…

在Fedora-Workstation-Live-x86_64-36-1.5中编译安装信使iptux0.7.6

在Fedora-Workstation-Live-x86_64-36-1.5中编译安装信使iptux0.7.6 https://github.com/iptux-src/iptux/tree/v0.7.6 下载信使iptux-0.7.6.zip&#xff0c;类似飞鸽传书ipmsg&#xff0c;已经尝试过0.8.3版本不成功 [rootfedora ~]# unzip /home/ruhong/download/iptux-0.7…

usb摄像头驱动-core层driver.c

usb摄像头驱动-core层driver.c 文章目录 usb摄像头驱动-core层driver.cusb_bus_typeusb_device_matchusb_uevent usb_register_driver 在ubuntu中接入罗技c920摄像头打印的信息如下&#xff1a; 在内核中&#xff0c;/driver/usb/core/driver.c 文件扮演了 USB 核心驱动程序管…

自动化遍历测试技术之android maxim遍历测试工具

这里写目录标题 一、问题1、例如app中存在问题2、解决方法3、改进策略4、自动遍历测试5、常见遍历工具与技术 二、android maxim 遍历测试工具策略使用环境预备命令行模式策略 三、android fastbot 遍历测试工具使用 一、问题 业务线众多 业务流程复杂 依赖传统券商一些资源 …

2023年最新软件测试面试题,自动化测试面试题,接口自动化测试面试题详解,对标大厂。

【软件测试面试题】 1、你的测试职业发展是什么&#xff1f;   测试经验越多&#xff0c;测试能力越高。所以我的职业发展是需要时间积累的&#xff0c;一步步向着高级测试工程师奔去。而且我也有初步的职业规划&#xff0c;前3年积累测试经验&#xff0c;按如何做好测试工程…

软件测试之性能测试

性能测试是与时间相关的。 主要内容 性能测试基础概念和术语介绍性能测试模型性能测试分类介绍性能测试实施与管理 性能测试基础 为什么要进行性能测试&#xff08;WHY&#xff09;&#xff08;最重要&#xff09; 应用程序是否能够很快的响应用户的要求&#xff1f;应用程…

【C程序设计】——程序=算法+数据结构

目录 &#x1f34a;&#x1f34a;一、什么是算法&#xff1f; &#x1f34a;&#x1f34a;二、简单的算法举例 &#x1f34a;&#x1f34a;三、算法的特性 &#x1f34a;&#x1f34a;四、怎样表示一个算法 一个程序主要包括以下两方面的信息&#xff1a; &#xff08;1&am…

软件测试项目实战经验附视频以及源码【商城项目,app项目,电商项目,银行项目,医药项目,金融项目】

前言&#xff1a; ​​大家好&#xff0c;我是凡叔。 很多初学的测试小白都在烦恼找不到合适的项目去练习&#xff0c;这也是难倒大部分测试小白的一个很常见的问题&#xff0c;项目经验确实是每一个测试非常宝贵的经验&#xff01;这里凡叔给大家找了一些常用的项目合集&…

数据结构与算法十二 图进阶

一 有向图 在实际生活中&#xff0c;很多应用相关的图都是有方向性的&#xff0c;最直观的就是网络&#xff0c;可以从A页面通过链接跳转到B页面&#xff0c;那么a和b连接的方向是a->b,但不能说是b->a,此时我们就需要使用有向图来解决这一类问题&#xff0c;它和我们之前…

行业集体迈进全屋智能,华为的“空间智能跃升”独领风骚?

智能家居近年来发展飞速&#xff0c;市场正在从最初的单品智能向全屋联动智能切换。 据IDC数据&#xff0c;2022年中国全屋智能市场销售额突破100亿元&#xff0c;同比增长54.9%。一个住宅往往由卧室、客厅、厨房、卫生间、餐厅等多个基本功能区组成&#xff0c;全屋智能便是在…

企业进行产品管理内训至少有这5大好处

企业需要重视产品管理&#xff0c;建立完善的产品管理流程和标准&#xff0c;提高员工的产品管理能力&#xff0c;以应对各种挑战和机遇&#xff0c;在企业进行内训是最好的方式。 企业进行产品管理内训的目的是为了提高员工的产品管理能力&#xff0c;从而实现以下几个方面的好…

ChatGPT创始人采访 | GPT-4报告中文版

关于采访OpenAl Co创始人 Greg Brockman的要点记录分析&#xff0c;先介绍Gpt-4的基本内容&#xff0c;然后说下采访的重点部分&#xff0c;最后读一下154页Gpt-4的技术报告&#xff0c;这个大家可以在官网下载文档后&#xff0c;百度翻译支持每人限量一次的PDF翻译。面对文心一…