当前位置: 首页 > article >正文

[ 一刷完结撒花!! ] 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.kler.cn/a/133570.html

相关文章:

  • CSP/信奥赛C++语法基础刷题训练(8):洛谷P5718:找最小值
  • 重卡穿越商都,ROG DAY 2024郑州站高燃来袭
  • 微澜:用 OceanBase 搭建基于知识图谱的实时资讯流的应用实践
  • 图论-代码随想录刷题记录[JAVA]
  • [Linux] Linux信号捕捉
  • 鸿蒙next版开发:相机开发-适配不同折叠状态的摄像头变更(ArkTS)
  • Fork项目新分支如何同步
  • upgrade k8s (by quqi99)
  • SEnet注意力机制(逐行代码注释讲解)
  • Golang中的闭包详解
  • 浅谈滑动窗口
  • Java Swing抽奖程序
  • 一句话总结敏捷实践中不同方法
  • Django 简单入门(一)
  • C++虚函数(定义,作用,原理,案例)
  • 实时人眼追踪、内置3D引擎,联想ThinkVision裸眼3D显示器创新四大应用场景
  • 【机器学习】 逻辑回归算法:原理、精确率、召回率、实例应用(癌症病例预测)
  • 【蓝桥杯选拔赛真题21】C++行李运费 第十二届蓝桥杯青少年创意编程大赛C++编程选拔赛真题解析
  • SpringSecurity+jwt使用
  • Python将已标注的两张图片进行上下拼接并修改、合并其对应的Labelme标注文件
  • 【Axure高保真原型】附件卡片
  • 深度解析 InterpretML:打开机器学习模型的黑箱
  • C++之旅(学习笔记)第8章 概念和泛型编程
  • 基于SSM+Vue的鲜花销售系统/网上花店系统
  • Unity Quaternion接口API的常用方法解析_unity基础开发教程
  • RobotFramework之用例执行时添加命令行参数(十三)