[ 一刷完结撒花!! ] Day50 力扣单调栈 : 503.下一个更大元素II |42. 接雨水 | 84.柱状图中最大的矩形

Day50 力扣单调栈 : 503.下一个更大元素II |42. 接雨水 | 84.柱状图中最大的矩形

  • 503.下一个更大元素II
    • 第一印象
    • 看完题解的思路
    • 实现中的困难
    • 感悟
    • 代码
  • 42. 接雨水
    • 第一印象
    • 看完题解的思路
      • 暴力解法
      • 单调栈解法
    • 实现中的困难
    • 感悟
    • 代码
  • 84.柱状图中最大的矩形
    • 第一印象
    • 看完题解的思路
    • 感悟
    • 代码

503.下一个更大元素II

这道题和 739. 每日温度 几乎如出一辙,可以自己尝试做一做

https://programmercarl.com/0503.%E4%B8%8B%E4%B8%80%E4%B8%AA%E6%9B%B4%E5%A4%A7%E5%85%83%E7%B4%A0II.html

第一印象

好, 那我就自己试试.

做出来了.

之前的单调栈题目就是遍历一遍数组, 找这一遍里右面最大的那个.

这道题说是循环数组, 其实也就是找两圈这个数组.

比如 5 2 3. 5是比3大的, 第一圈里只能找出3比2大, 第二圈才能找到5比3大.

有两个方式我觉得, 一个是找两圈数组, 也是我下面代码写的.

或者直接改造这个数组, 变成自己的两倍大小, 元素是自己的两遍, 比如5 2 3 5 2 3.

我自己写的代码,请看vcr:

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int[] result = new int[nums.length];
        Deque<Integer> stack = new LinkedList<>();
        int loop = 1;

        Arrays.fill(result, -1);
        stack.push(0);

        for (int i = 1; i < nums.length; i++) {
            if (nums[i] <= nums[stack.peek()]) {
                stack.push(i);
            } else {
                while (!stack.isEmpty() && nums[i] > nums[stack.peek()]) {
                    //弹出栈顶元素,是下标
                    int index = stack.pop();
                    //记录结果
                    result[index] = nums[i];
                }
                stack.push(i);
            }
            if (loop == 1 && i == nums.length - 1) {
                //因为循环结束 i++, 而第二圈应该从 i=0 开始.
                i = -1;
                loop--;
                continue;
            }
        }

        return result;
    }
}

看完题解的思路

确实, 就是我上面说的两种方式, 但是扩大数组的复杂度就是O(n)了

而走两圈的方式, 卡哥比我做的要聪明一些, 用 % .

其实我也想到了, 就是感觉容易混乱在数学里.

for(int i = 0; i < 2*size; i++) {
    while(!st.empty() && nums[i % size] > nums[st.peek()]) {
        result[st.peek()] = nums[i % size];//更新result
        st.pop();//弹出栈顶
    }
    st.push(i % size);
}

实现中的困难

在我的代码里

if (loop == 1 && i == nums.length - 1) {
    //因为循环结束 i++, 而第二圈应该从 i=0 开始.
    i = -1;
    loop--;
    continue;
}

走两圈的逻辑, 应该让 i = -1 ,而不是 i = 0

因为for循环结束之后会有 i++ .

感悟

虽然麻烦了点, 但我还是做对了

代码

上面给出了.

42. 接雨水

接雨水这道题目是 面试中特别高频的一道题,也是单调栈 应用的题目,大家好好做做。

建议是掌握 双指针 和单调栈,因为在面试中 写出单调栈可能 有点难度,但双指针思路更直接一些。

在时间紧张的情况有,能写出双指针法也是不错的,然后可以和面试官在慢慢讨论如何优化。
https://programmercarl.com/0042.%E6%8E%A5%E9%9B%A8%E6%B0%B4.html

第一印象

想了一下, 感觉情况比较多

  • 两边都比自己高, 选小的那个 - 自己 = 水
  • 两边有一边比自己高, ??? 这种情况咋办呢
  • 两边都没自己高, 没有水

看题解!

看完题解的思路

暴力解法

找到右边第一个比自己大的, 左面第一个比自己大的. 就能算出来自己这里有多少水. 硬暴力去算.

单调栈解法

对于单调栈, 前面的题目已经会了找右边更大的(栈元素递增), 右边更小的(栈元素递减).

那怎么找左边更大的呢? 我第一反应是逆序遍历数组.

可以, 但有更好的方式. 这道题就是找到三个柱子, 左边更大+自己+右边更大

自己就是栈顶元素, 拿来的元素 i 可能是右边更大的(不更大就push入栈了), 那左边更大的在哪?

其实就是栈顶下面的那个元素, 如图:

因为栈里是递增的, 我自己下面那个就是 我左面第一个更大的呀

太tm巧妙了!!!
在这里插入图片描述

看完了, 会了

重要的是, 这道题是横向的求雨水面积, 我一直想的是 纵向的求雨水面积.

我听完题解还是觉得纵向的求没有错

但是细看

在这里插入图片描述

这个地方左面第一个更高和右边第一个更高最小的是 1

自己是 0

那么纵向雨水就是 1 , 算不到红色方块那里的

实现中的困难

思路清晰就不难实现

感悟

确实比较难

一个是纵向和横向计算容易糊涂

我也觉得横向计算的话, 下图的两个地方不会重复吗?

在这里插入图片描述

不会的

因为第二个 1 拿来的时候, 就比 0大了, 就会进行一次计算.

算出雨水量 1, 并把 0 pop掉, 第二个 1 push进去

拿来 3 的时候, 比栈顶的第二个 1 更大, 就会进行一次计算.

但是栈顶下面的元素就是第一个 1 , 在计算高度的时候, 取第一个 1 和 3 更小的那个 再减去栈顶的第二个 1 . 高度就是0 , 就白计算了.

然后第二个 1 pop

这个时候栈顶是第一个 1 , 栈顶下面的元素是 2 , 就会进行一次计算.

算出 高度 1 ,宽度 3, 雨水量 3.

然后pop掉第一个 1, 3 比栈顶的 2 还要大. 但是呢, pop 2 之后, 栈就空了, 就不会计算, 只是 pop 了2.

最后 3 push进栈, 再去算后面的元素.

顿悟 咯~~~~~~~~~~~~~

代码

class Solution {
   public int trap(int[] height) {
       int result = 0;
       Deque<Integer> stack = new LinkedList<>();

       stack.push(0);
       for (int i = 1; i < height.length; i++) {
           if (height[i] < height[stack.peek()]) {
               stack.push(i);
           } else if (height[i] == height[stack.peek()]) {
               stack.pop();
               stack.push(i);
           } else {
               while (!stack.isEmpty() && height[i] > height[stack.peek()]) {
                   int curIndex = stack.pop();
                   int cur = height[curIndex];
                   //如果还有左边的话
                   if (!stack.isEmpty()) {
                       int min = Math.min(height[i], height[stack.peek()]);
                       //求高度
                       int h = min - cur;
                       int w = i - stack.peek() - 1;
                       //我纵向的求
                       int rain = w * h;
                       result += rain;
                   }
               }
               stack.push(i);
           }
       }
       return result;
   }
}

84.柱状图中最大的矩形

https://programmercarl.com/0084.%E6%9F%B1%E7%8A%B6%E5%9B%BE%E4%B8%AD%E6%9C%80%E5%A4%A7%E7%9A%84%E7%9F%A9%E5%BD%A2.html

今天是训练营最后一天,恭喜坚持两个月的录友们,接下来可以写一篇自己
代码随想录一刷的总结。好好回顾一下,这两个月自己的博客内容,以及自己的收获。

第一印象

我也觉得是要找左右两边第一个更小的

题解也是这么写的.

但是为啥呢

我只是有一种直觉, 它不像接雨水那样比较直观.

看完题解的思路

我明白了, 拿题里的例子说明一下

在这里插入图片描述

比如拿到元素 1 , 向左找第一个更小, 没有. 向右找第一个更小, 没有.

所以 1 的高度, 贯穿整个数组.

比如拿到 5, 向左找第一个更小是 1 . 向右找第一个更小, 是2.

那么 5 的高度就是从 1 贯穿到 2, 宽度其实就是 2 的下标 - 1 的下标 - 1.

这就是为什么要向左向右找第一个更小的原因.

也就是看现在这个元素(是高度)能贯穿多长(宽度).

然后这道题还有个处理技巧, 给数组扩容, 头和尾都加上高度 0 .

这样就方便算头和尾的元素的面积. 而且还能算出那个 最矮 的 1 的面积. 如图

在这里插入图片描述

但这里又会产生一个问题

对于相等的情况我自己写的时候的处理是, 什么都不做

if (heights[i] > heights[stack.peek()]) {
    stack.push(i);
} else if (heights[i] == heights[stack.peek()]) {
    //不处理, 相同的话先放进来的元素会让面积更大
} else {

比如 1 4 4 3.

肯定是第一个 4 算出的面积更大, 第二个 4 就是 4*1, 第一个 4 是 4 * 2.

所以遇到相同元素, 什么都不做就行了.

但是由于头加了 0 , 如果第一个高度还是 0的话就会出现错误.

在这里插入图片描述
扩容之后的数组是 0 0 9 0

因为相同元素什么都不干, 所以第二个 0 不会压入栈

在这里插入图片描述

这样计算 这个 9 的时候, 宽度就是 2 了. 就错了!!!

所以不能让相同元素什么都不干.

而是像接雨水一样, pop 在push 可以, 只push 也可以

还拿 1 4 4 3 的例子

pop 再 push, 栈顶就是第二个 4 , 下面是 1 . 拿到元素 3 的时候, 计算的宽度也是 3 - 0 - 1 = 2. 算的是到 1 到 3 的宽度. 是大的那个.

只push呢, 栈顶是第二个4 然后第一个4, 然后是 1 . 就是先算一个小的面积, 再算大的.

都是对的, 就是什么都不做不行.

感悟

这题也挺奇妙, 但是栈里递增递减不是什么难的.

主要就是头和尾 + 0 的小技巧

代码

class Solution {
    public int largestRectangleArea(int[] heights) {
        int maxArea = 0;
        Deque<Integer> stack = new LinkedList<>();

        // 数组扩容,在头和尾各加入一个元素
        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;

        stack.push(0);
        for (int i = 1; i < heights.length; i++) {
            if (heights[i] > heights[stack.peek()]) {
                stack.push(i);
            } else if (heights[i] == heights[stack.peek()]) {
                stack.pop();
                stack.push(i);
            } else {
                while (!stack.isEmpty() && heights[i] < heights[stack.peek()]) {
                    int curIndex = stack.pop();
                    int cur = heights[curIndex];

                    if (!stack.isEmpty()) {
                        int weight = i - stack.peek() - 1;
                        int area = cur * weight;
                        System.out.println(area + "=" + cur + "*" + weight);
                        maxArea = Math.max(maxArea, area);
                    }    
                }
                stack.push(i);
            }
        }

        return maxArea;
    }
}

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

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

相关文章

计算机视觉与机器学习D1

计算机视觉简介 技术背景 了解人工智能方向、热点 目前人工智能的技术方向有&#xff1a; 1、计算机视觉——计算机视觉(CV)是指机器感知环境的能力&#xff1b;这一技术类别中的经典任务有图像形成、图像处理、图像提取和图像的三维推理。物体检测和人脸识别是其比较成功…

Ubuntu20.04 安装微信 【wine方式安装】推荐

安装步骤: 第一步:安装 WineHQ 安装包 先安装wine,根据官网指导安装即可。下载 - WineHQ Wikihttps://wiki.winehq.org/Download_zhcn 如果您之前安装过来自其他仓库的 Wine 安装包,请在尝试安装 WineHQ 安装包之前删除它及依赖它的所有安装包(如:wine-mono、wine-gec…

深度学习二维码识别 计算机竞赛

文章目录 0 前言2 二维码基础概念2.1 二维码介绍2.2 QRCode2.3 QRCode 特点 3 机器视觉二维码识别技术3.1 二维码的识别流程3.2 二维码定位3.3 常用的扫描方法 4 深度学习二维码识别4.1 部分关键代码 5 测试结果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天…

C++多线程编程(1):线程的创建方式

文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 文章目录 进行与线程C中如何实现多线程创建线程的多种方式无参函数lambda表达式常成员函数not常成员引用函数智能指针仿函数类的普通成员函数综合测试 进行与线程 多线程是指多个线程并发执行的过程。 进程与线程的关系&…

使用Qt实现多人聊天工作室

目录 1、项目背景 2、技术分析 3、架构设计 3、1 服务器架构 3.1.1 模块划分 3.1.2 模块之间的交互 3、2 客户端架构 3.2.1 模块划分 3.2.2 模块之间交互 4、实现过程 4、1 功能实现 4.1.1 用户登录注册功能​编辑 4.1.2 用户主界面功能 4、2 设计实现 4.2.1 登录…

传输层协议-TCP协议

目录 TCP协议格式理解可靠性序号与确认序号16位窗口大小六个标志位连接管理机制三次握手四次挥手 确认应答机制&#xff08;ACK&#xff09;超时空重传机制流量控制滑动窗口拥塞控制延迟应答捎带应答面向字节流粘包问题TCP异常情况TCP小结基于TCP应用层协议TCP/UDP对比用UDP实现…

程序的编译链接以及装载

目录 一、预处理 二、编译 三、汇编 四、链接 五、装载 一、预处理 读取c源程序&#xff0c;对其中的伪指令&#xff08;以#开头的指令&#xff09;和特殊符号进行处理&#xff0c; 伪指令主要包括以下五个方面&#xff1a; 宏定义指令&#xff0c;如#define Name Token…

如何定位el-tree中的树节点当父元素滚动时如何定位子元素

使用到的方法 Element 接口的 scrollIntoView() 方法会滚动元素的父容器&#xff0c;使被调用 scrollIntoView() 的元素对用户可见。 参数 alignToTop可选 一个布尔值&#xff1a; 如果为 true&#xff0c;元素的顶端将和其所在滚动区的可视区域的顶端对齐。相应的 scrollIntoV…

基于冠状病毒群体免疫算法优化概率神经网络PNN的分类预测 - 附代码

基于冠状病毒群体免疫算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于冠状病毒群体免疫算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于冠状病毒群体免疫优化的PNN网络5.测试结果6.参考文献7.Matlab代码 …

再高级的打工人也只是打工人!

再高级的打工人也只是打工人&#xff01; OpenAI CEO 奥特曼被罢免的事情人尽皆知「虽然&#xff0c;今天又复职了。。」&#xff0c;我们能从中学到什么呢&#xff1f; CEO 也能被裁&#xff0c;这应该是最近几年被裁名单里面&#xff0c;职级最高的一个人了吧。你再也不用担…

2023最新最全【Nacos】零基础安装教程

一、下载Nacos1.4.1 二、单机版本安装 2.1 将下载的nacos安装包传输到服务器2.2 解压文件2.3 进入bin目录下 单机版本启动2.4 关闭nacos2.5 访问Nacos地址 IP&#xff1a;8848/nacos 三、集群版本的安装 3.1 复制nacos安装包&#xff0c;修改为nacos8849&#xff0c;nacos88…

cesium 图片旋转

cesium 图片旋转 1、实现思路 用cesium 中 ellipse 方法来加载圆型&#xff0c;改变 material 材质 用 ImageMaterialProperty 属性来加在图片&#xff0c;实时改变rotation&#xff0c;stRotation属性来实现旋转 2、源码实现 <!DOCTYPE html> <html lang"en&…

自定义业务异常处理类加入全局处理器中

自定义业务异常处理类并将其加入全局异常处理器&#xff0c;从而避免业务层直接处理异常造成代码污染&#xff0c;达到业务清晰简洁。 描述 在进行分类模块开发时&#xff0c;删除某个分类时当分类关联了菜品和套餐时&#xff0c;是不允许删除的。我们在管理端删除的时候会提示…

lectin

PSGL-1 ; selectin O-linked glycosylation | Detailed Pedia PSGL-1 has several O-glycans to extend the ligand away from the cell surface. An sLex epitope allows interactions with the receptor for leukocyte localisation. 分类 --Recognition by Animal Lectins…

【linux网络】解读FTP文件传输服务器配置,揭秘百度云下载限速原理

目录 一、FTP文件传输协议 1.1FTP工作原理 1.2两种模式介绍 1.3FTP状态码 1.4FTP的三种用户分类 二、vsftpd软件的介绍 2.1服务端介绍 2.2不同操作系统下的客户端登录操作 三、vsftpd的常见配置 3.1修改默认的命令端口 3.2限制匿名用户登录&#xff08;系统原本是默…

实验五:Java多线程程序设计

一、线程接力 编写一个应用程序&#xff0c;除了主线程外&#xff0c;还有三个线程&#xff1a;first、second和third。first负责模拟一个红色的按钮从坐标&#xff08;10&#xff0c;60&#xff09;运动到&#xff08;100&#xff0c;60&#xff09;&#xff1b;second负责模…

安顿APP3.0全新升级,引领智能穿戴健康革新,专注预警疾病风险

随着人们生活水平的提高和工作压力的增加&#xff0c;心脑血管疾病已经成为现代社会的严重问题&#xff0c;特别是心梗、脑卒中等疾病已经开始夺去年轻人的生命。 据报道&#xff0c;近年来&#xff0c;多位年轻人因心脑血管疾病突发去世&#xff0c;如42岁的知名男演员、30岁的…

【STL】:反向迭代器

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关反向迭代器的模拟实现&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通…

Web之JavaScript(jQuery)笔记

Web之HTML、CSS、JavaScript 三、JavaScriptJS调试变量自定义函数数据类型及转换运算符优先级内置函数数组事件DOM(Document Object Model 文档对象模型)jQuery Web之HTML笔记 Web之CSS笔记 三、JavaScript JavaScript&#xff08;简称“JS”&#xff09;是一种轻量级的面向对…

计算机组成原理-双端口RAM和多模块存储器

文章目录 存取周期总览双端口RAM多体并行存储器低地址交叉编址有多少个存储体合适&#xff08;体号&#xff09;多模块存储器&#xff08;多体存储器&#xff09;总结实际场景 存取周期 总览 双端口RAM RAM&#xff1a;用于主存或高速缓存&#xff0c;断电数据丢失 多体并行…
最新文章