二刷力扣——单调栈

news2024/9/23 7:21:07

739. 每日温度

单调栈应该从栈底到栈顶 是递减的。

找下一个更大的 ,用递减单调栈,就可以确定在栈里面的每个比当前元素i小的元素,下一个更大的就是这个i,然后弹出并记录;然后当前元素i入栈,仍然满足递减要求。最后留在栈里面的是没有下一个更大的值的,符合要求。

找下一个更小的用递增单调栈同理。

result[被弹出下标]=使他弹出下标-被弹出下标。

复习一下Java集合知识:来自Java集合详解-CSDN博客

LinkedList 可以使用多种接口进行声明,包括但不限于:

  • Deque 接口:Deque<Integer> deque = new LinkedList<>();: 双端队列的操作,包括栈和队列的功能。
  • Queue 接口:Queue<Integer> queue = new LinkedList<>();: 适合实现队列的操作,包括 offer、poll、peek 等方法。
  • List 接口:List<Integer> list = new LinkedList<>();: 通用的集合操作,如添加、删除、获取元素等。
  • Collection 接口:Collection<Integer> collection = new LinkedList<>();: 这种声明方式表示通用的集合操作,适合需要简单地操作元素集合的场景。
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int n=temperatures.length;
        Deque<Integer> stack=new LinkedList<>();//放下标,
        int [] result =new int[n];
        for(int i=0;i<n;++i)
        {
            int temp=temperatures[i];
            while(!stack.isEmpty() && temperatures[stack.peek()]<temp)
            {
                int a=stack.pop();//小,被弹出
                result[a]=i-a;
            }
            //找到位置
            stack.push(i);
        }
        return result;
    }
}

时间、空间O(n)

496. 下一个更大元素 I

跟上一题区别是,先用单调栈把nums2处理,较小的元素弹出来的时候要找到当前元素i在nums1的位置,然后给到result。

这里不算下标而且数组里面没有重复元素,所以单调栈里面可以放元素值。

另外result一开始都赋值-1。最后可能nums1里有几个是没有下一个更大值的。

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int m=nums1.length ,n=nums2.length ;
        Deque<Integer> stack=new LinkedList<Integer>();
        int[] result=new int[m];
        HashMap<Integer,Integer> hash=new HashMap<>();// 放元素
        for(int i=0;i<m;++i)
        {
            hash.put(nums1[i],i);
        }
        for(int i=0;i<m ;++i)
        {
            result[i]=-1;
        }
        for(int i=0;i<n;++i)
        {
            int tmp=nums2[i];
            while(!stack.isEmpty() && stack.peek()<tmp)
            {
                int a=stack.pop();//被弹出的元素a
                int pos=hash.getOrDefault(a,-1);
                if(pos!=-1)result[pos]=tmp;
            }
            stack.push(tmp);
        }
        return result;
    }
}

时间是O(m+n)。初始化result和hash的是O(m),单调栈循环是O(n),因为用的HashMap,查找O(1)。

503. 下一个更大元素 II

要求循环地搜索元素的下一个更大的数,其实就是单调栈要走两次nums数组。

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int n=nums.length;
        int[] result=new int[n];
        Deque<Integer> stack=new LinkedList<>();
        Arrays.fill(result,-1);
        for(int j=0;j<2*n;++j)
        {
            int i=j%n;
            int num=nums[i];
            while(!stack.isEmpty() && nums[stack.peek()]<num)
            {
                int a=stack.pop();
                result[a]=nums[i];
            }
            stack.push(i);
        }
        return result;
    }
}

42. 接雨水

1、以列为单位,每列都找自己左边(包含自己)的最高柱子 l 和自己右边(包含自己)的最高柱子 r,就形成一个凹槽。然后每一列应该装下的雨水数就应该是(min(l , r) - 自己柱子高)*宽。所以主要关注l 和 r 怎么计算。

1.1、DP

维护一个lefts和rights数组,lefts[i]是l,rights[i]就是r。

那么递推公式就是:(min(lefts[i] , rights[i]) - 自己柱子高)*宽

关键得到这两个数组,还是很直观的,lefts涉及到的是i及左边的,所以从左往右递推;rights从右往左。

class Solution {
    public int trap(int[] height) {
        int n=height.length;
        int count=0;
        int[] lefts=new int[n];
        int[] rights=new int[n];
        lefts[0]=height[0];
        for(int i=1;i<n;++i)
        {
            lefts[i]=Math.max(lefts[i-1],height[i]);
        }
        rights[n-1]=height[n-1];
        for(int i=n-2;i>=0;--i)
        {
            rights[i]=Math.max(rights[i+1],height[i]);
        }
        for(int i=0;i<n;++i)
        {
            count+=(Math.min(lefts[i],rights[i])-height[i]);
        }
        return count;
    }
}

时间空间:O(n)

1.2、双指针

用两个指针left、right从两端,逐渐逼近,逼近的判断条件是:left指向的和right指向的对比,哪一方小,就走一步。

①这里对于红框的判断不能理解,为什么当前的哪一方小,就能确定这一方的最大值更小呢?

下面这个说法很形象。其实就是A队B队比赛,遍历过了的都是输了的(或者平局的),假如B队的curB赢了A队的curA,所以目前MAX_B(其实就是curB)是目前两队最强。即A队最强的其实是输给B队过的了,所以A队最强不如B队最强。可以确定MAX_B=curB>MAX_A。A赢B就同理。

②又想:假如B队赢了A队,可以确定出现了一个凹槽:A队最高、curA、B队最高。这个凹槽的两端一定是我们要求的吗?

A队最高肯定是所要求的左边最高柱子l ,B队最高不一定是右边最高柱子 r。假如curA和curB之间还有比MAX_A更矮的柱子,压根不考虑因为我们是先找两端最高的,再在两个最高的里找最小的;假如有比MAX_B更高的柱子,那取Min值仍然还是MAX_A。A赢B就同理。

总的来说,哪一方输了,哪一方的输家就可以确定他的雨水了。决定这个雨水高度的就是他这一方的最强者(但是比另一方的最强者弱)。

可以用MAX_B>MAX_A,用height[a]<height[b]感觉更能体现这个“比赛”的过程吧,当前选手的大小。

class Solution {
    public int trap(int[] height) {
        int n=height.length;
        int a=0,b=n-1;//对手
        int MAX_A=0,MAX_B=0;//一个队里面已比对手的最强者
        int count=0;
        while(a<b)
        {//更新最强者,要包括当前选手
            MAX_A=Math.max(MAX_A,height[a]);
            MAX_B=Math.max(MAX_B,height[b]);
            int yushui=0;//输的一方收集雨水
            if(height[a]<height[b])//b整体赢了
            {
                yushui=MAX_A-height[a];//注意凹槽两端
                a++;//输的下场,派下一个
            }
            else if(height[a]>=height[b])
            {
                yushui=MAX_B-height[b];
                b--;        
            }
            count+=yushui;
        }
        return count;
    }
}

时间O(n)空间O(1)

2、以行为单位

2.1 单调栈

以列为单位求,每个柱子的雨水是一次性求好,需要提前知道左边最高柱子和右边最高柱子,这形成凹槽1;

以行为单位求,是求以每个柱子为底的这一行能接的雨水,这一行雨水可以到的高度,应该是最接近自己的柱子的最小高度。即Min(上一个更大,下一个更大)。这是凹槽2。

这是按行求 和 按列求 的主要区别。

这一行,宽是 下一个更大和上一个更大的 下标间隔。高是Min值和中间 a 的差。

class Solution {
    public int trap(int[] height) {
        int n=height.length;
        int count=0;
        Deque<Integer> stack=new LinkedList<Integer> ();
        for(int i=0;i<n;++i)
        {
            int tmp=height[i];
            while(!stack.isEmpty() && height[stack.peek()]<tmp)//凹槽
            {
                int a=stack.pop();//l=栈底,a,r=height[i]
                if(!stack.isEmpty()){
                    int l=stack.peek();
                    int r=i;
                    int kuan=r-l-1;
                    int gao=Math.min(height[l],height[r])-height[a];
                    count+=kuan*gao;
                }
                
            }
            stack.push(i);
        }
        return count;
    }
}

84. 柱状图中最大的矩形

貌似只能以行为单位,所以只看单调栈方法。

需要找到元素i的上一个更小的元素leftmin和下一个更小的元素rightmin,这样leftmin和rightmin之间的元素都比当前元素i更大,那么矩形的宽就是中间的这些元素:可以从leftmin+1延伸到rightmin-1,长即为height[i]。

怎么确定矩形的宽 和高?宽有左边界和右边界,这里感觉应该每次固定一边然后讨论另一边。这里固定右边界,是遍历的i,也就是对于每个柱子i 都计算,以这个元素为右边界 ,矩形的最大面积。知道右边界了,要让面积最大左边界应该是多少呢?其实应该找上一个更小值l 。这样 (l,i) 之间都是比i 高或者相等的柱子,这就是固定i 为右边界的最大面积。

怎么保证能处理所有元素?使用单调栈,pop一个元素就算这个元素右边界的矩形。题目说柱子都是>=0的,那么在最后的时候 ,单调栈再插入一个0,就能把栈里面的所有元素都pop出来,就都处理到了

单调栈 的顺序 应该是 需要 严格单调递增的。因为栈不为空要找上一个更小值l 。之前的题事实上只在单个>、<的时候要出栈,因为只需要求下一个更大/更小。上面的接雨水也是栈顶=当前的话没关系,相减是0 没影响。但是这里得严格递增。

过程是:当 i 元素 <= 栈顶的 a,这个时候这个i 元素就是右边界,我们要讨论单调栈里面所有比这个元素大的元素为高的矩形面积。现在 两种情况:

1、栈顶a弹出,栈为空了,说明什么?在heights里面,a的左边没有比他严格小于的l ,都是>=他的,而a 的下一个更小值又是i,所以区间是[0,i-1)所以宽就是i;

2、如果栈不为空,那就是存在l ,这个l 就是a弹出之后的新栈顶(比a 要小的)。所以矩形区间是(新栈顶=l,i)。

class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        Deque<Integer> stack = new LinkedList<>(); // 存储柱子的索引
        int maxArea = 0;
        
        for (int i = 0; i <= n; ++i) {
            int h = (i == n) ? 0 : heights[i]; // 最后加入一个高度为0的柱子,确保处理完所有柱子
            
            while (!stack.isEmpty() && heights[stack.peek()] >= h) {
                int a=stack.pop();
                int height = heights[a]; // 当前出栈的柱子高度
                // 计算宽度:是i代表前面i个都是>=height的。否则矩形宽是(上一个更小的,i-1]
                int width = stack.isEmpty() ? i : i - stack.peek() - 1; 
                
                maxArea = Math.max(maxArea, height * width);
                System.out.println(maxArea);
            }
            stack.push(i); // 当前柱子的索引入栈
        }
        
        return maxArea;
    }
}

主要是 :确定一边求另一边的思想、求上一个更小值用单调栈、为了处理所有元素添加一个最小值0。

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

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

相关文章

AcWing 1550:完全二叉搜索树

【题目来源】https://www.acwing.com/problem/content/1552/【题目描述】二叉搜索树 (BST) 递归定义为具有以下属性的二叉树&#xff1a; &#xff08;1&#xff09;若它的左子树不空&#xff0c;则左子树上所有结点的值均小于它的根结点的值 &#xff08;2&#xff09;若它的右…

BS结构的毕业设计题目管理系统-计算机毕业设计源码92342

目 录 摘要 1 绪论 1.1 研究背景 1.2目的及意义 1.3论文结构与章节安排 2 毕业设计题目管理系统设计分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用例分…

【C++】开源:地图投影和坐标转换proj库配置使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍地图投影和坐标转换proj库配置使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&a…

二、从多臂老虎机看强化学习

二、从多臂老虎机看强化学习 2.1 多臂老虎机问题2.1.1 问题定义2.2.2 问题建模2.2.3 累积懊悔2.2.4 估计期望奖励 2.2 强化学习中的探索与利用平衡2.3 贪心策略2.4 上置信界算法2.5 汤普森采样算法 2.1 多臂老虎机问题 2.1.1 问题定义 在多臂老虎机(mutil-armed bandit, MAB)问…

linux 服务器数据备份 和 mysql 数据迁移

查看域名ip 查看程序所处文件位置 list open files 1、 lsof -i :port 查看端口获取进程 pid 2、lsof -i pid 1、scp 下载服务器文件到本地 security copy protocol 2、导出服务器 mysql 数据库&#xff08;表&#xff09;到本地 mysqldump是MySQL自带的一个实用程序&…

半同步主从复制

半同步主从复制的概念 半同步主从复制&#xff08;Semisynchronous Replication, SBR&#xff09;是MySQL数据库中的一种数据复制方式&#xff0c;它在异步复制的基础上增加了一定程度的同步性&#xff0c;旨在提高数据安全性&#xff0c;减少数据丢失的风险。 半同步主从复制…

Facebook群发消息API接口的申请流程详解!

Facebook 群发消息api接口如何集成&#xff1f;怎么使用API接口&#xff1f; 在现代社交媒体营销中&#xff0c;群发消息是与客户保持互动的重要工具。Facebook群发消息API接口提供了一种有效的方法来实现这一目标。本文将详细介绍如何申请Facebook群发消息API接口的具体步骤和…

51单片机基础10——串口实验

串口实验 51单片机串口实验1. 软硬件条件2. 串口实验2.1 单片机与PC 发送字符2.1.1 效果2.1.2 代码2.1.3 优化 2.3 串口接收数据(指令控制单片机)2.3.1 非中断方式实现2.3.2 中断方式实现 51单片机串口实验 1. 软硬件条件 单片机型号&#xff1a;STC89C52RC开发环境&#xff…

【微信小程序开发】小程序更新、页面生命周期、用户信息获取应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

小白 | Linux安装python3

一、更新包列表 首先&#xff0c;确保你的包管理器是最新的&#xff1a; sudo apt update 二、安装 Python 3 安装 Python 3 以及常用的开发工具 sudo apt install python3 python3-pip python3-venv 三、验证安装 python3 --version

GlusterFS分布式存储系统

GlusterFS分布式存储系统 一&#xff0c;分布式文件系统理论基础 1.1 分布式文件系统出现 计算机通过文件系统管理&#xff0c;存储数据&#xff0c;而现在数据信息爆炸的时代中人们可以获取的数据成指数倍的增长&#xff0c;单纯通过增加硬盘个数来扩展计算机文件系统的存储…

Apache Seata应用侧启动过程剖析——RM TM如何与TC建立连接

本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 Apache Seata应用侧启动过程剖析——RM & TM如何与TC建立连接 前言 看过官网 README 的第…

2-5 softmax 回归的简洁实现

我们发现通过深度学习框架的高级API能够使实现线性回归变得更加容易。 同样&#xff0c;通过深度学习框架的高级API也能更方便地实现softmax回归模型。 本节如在上节中一样&#xff0c; 继续使用Fashion-MNIST数据集&#xff0c;并保持批量大小为256。 import torch from torc…

【vue组件库搭建04】使用vitepress搭建站点并部署到github

前言 基于vitePress搭建文档站点&#xff0c;使用github pages进行部署 安装VitePress 1.Node.js 18 及以上版本 2.npm add -D vitepress 3.npx vitepress init 4.将需要回答几个简单的问题&#xff1a; ┌ Welcome to VitePress! │ ◇ Where should VitePress initi…

ROS 2官方文档(基于humble版本)学习笔记(四)

ROS 2官方文档&#xff08;基于humble版本&#xff09;学习笔记&#xff08;四&#xff09; 2.客户端库使用colcon构建包&#xff08;package&#xff09;创建工作空间&#xff08;workspace&#xff09;构建工作空间执行测试&#xff08;tests&#xff09;导入环境&#xff08…

Java项目:基于SSM框架实现的会员积分管理系统分前后台【ssm+B/S架构+源码+数据库+毕业论文】

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

springcloud-alibba之FeignClient

代码地址&#xff1a;springcloud系列: springcloud 组件分析拆解 1.FeignClient的集成 springboot版本&#xff1a;3.1.5 springcloud组件版本&#xff1a;2022.0.4 nacos客户端的版本&#xff1a;2.3.2 1.引pom 这里引入了nacos和feginclient的版本 <dependency>…

moonlight+sunshine+ParsecVDisplay ipad8-windows 局域网串流

1.sunshine PC 安装 2.设置任意账户密码登录 3.setting 里 network启用UPNP IPV4IPV6 save apply 4.ParsecVDisplay虚拟显示器安装 5.ipad appstore download moonlight 6.以ipad 8 为例 2160*1620屏幕分辨率 7.ParsecVDisplay里面 custom设置2160*1620 240hz&#xff0c;…

org.springframework.jdbc.BadSqlGrammarException异常

Bug 记录 概述 在执行定时任务更新电子书统计信息时&#xff0c;遇到了 org.springframework.jdbc.BadSqlGrammarException 异常&#xff0c;具体表现为 SQL 函数 count 被错误地解析为自定义函数 wiki.count&#xff0c;导致数据库更新操作失败。 详细描述 错误信息&#x…

本地部署到服务器上的资源路径问题

本地部署到服务器上的资源路径问题 服务器端的源代码的静态资源目录层级 当使用Thymeleaf时&#xff0c;在templates的目录下为返回的html页面&#xff0c;下面以两个例子解释当将代码部署到tomcat时访问资源的路径配置问题 例子一 index.html&#xff08;在templates的根目录…