【力扣刷题】Day32——单调栈专题

news2024/11/20 15:36:36

文章目录

  • 单调栈
    • 1.每日温度
    • 2.下一个更大元素 I
    • 3.下一个更大元素II
    • 4. 接雨水
    • 5.柱状图中最大的矩形


单调栈

单调栈基础知识回顾:单调栈与单调队列_塔塔开!!!的博客-CSDN博客_单调栈 单调队列

单调栈一般模板:

int[] stk = new int[N] //Stack<Integer> stk;
int tt = 0;

// 模板1....
for(int i = 0; i < nums.size(); i++){
    
    while(tt != 0 && stk[tt] > nums[i]){
		tt --;// 出栈
        .....// 业务处理        
    }
	stk[++ tt] = nums[i];// 数/下标1入栈
}


// 模板2....
for(int i = 0; i < nums.size(); i++){
    
	while(tt != 0 && stk[tt] <= nums[i]) tt --;// 出栈
	if(tt == 0){
        .....
    }
	else .....// 业务:(记录左边/右边 第一个比它小/大的数等等业务)
    stk[++ tt] = nums[i];// 数/下标入栈
}

1.每日温度

题目链接:739. 每日温度 - 力扣(LeetCode)

Code

class Solution {
    /**
        思路:从右往左遍历 实现单调递减栈,记录当前每一个数 右边第一个大于它的数的位置(单调递减栈)
     */
     int N = 100010;
     int[] stk = new int[N];
     int tt;
     int[] pos = new int[N];
    public int[] dailyTemperatures(int[] a) {
        int n = a.length;
        for(int i = n - 1; i >= 0; i --){
            while(tt != 0 && a[stk[tt]] <= a[i]) tt --;
            if(tt == 0) pos[i] = 0;
            else pos[i] = stk[tt];// 记录右边大于它的第一个数的位置
            stk[++ tt] = i;
        }
        int[] res = new int[n];
        for(int i = 0; i < n; i ++){
            if(pos[i] == 0){
                res[i] = 0;
            }else {
                res[i] = pos[i] - i;// 计算位于右边的第几个
            }
        }
        return res;
    }
}

2.下一个更大元素 I

题目链接:496. 下一个更大元素 I - 力扣(LeetCode)

思路一:枚举 + 哈希表

我们用哈希表来记录,nums2[]nums1[i]出现的位置,然后我们两重遍历,一重循环遍历nums1,二重循环从nums1[i]对应在nums2的位置j开始遍历,然后找到第一个大于nums1[i]mums2[j],找到则直接跳出,否则不存在。

Code

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        Map<Integer, Integer> mp = new HashMap<>();
        int n = nums1.length;
        int m = nums2.length;

        int[] res = new int[n];
        int cnt = 0;
        for(int i = 0; i < m; i ++) mp.put(nums2[i], i);
        for(int i = 0; i < n; i ++){
            boolean f = false;
            for(int j = mp.get(nums1[i]); j < m; j ++){
                if(nums2[j] > nums1[i]){
                    res[cnt ++] = nums2[j];
                    f = true;
                    break;
                }
            }
            if(!f) res[cnt ++] = -1;
        }
        return res;
    }
}

思路二:单调栈 + 哈希表——跟每日温度一样求的是右边第一个大于它的数。(从右往左)遍历nums2,单调栈求得右边第一个大于它的数,然后用哈希表记录下来,最后我们在遍历nums1即可。

Code

class Solution {
    int N = 1010;
    int[] stk = new int[N];
    int tt = 0;
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int n = nums1.length;
        int m = nums2.length;

        // 从右往左遍历 找出右边大于它的 第一个数
        Map<Integer, Integer> mp = new HashMap<>();
        for(int i = m - 1; i >= 0; i --){
            while(tt != 0 && stk[tt] <= nums2[i]) tt --;
            if(tt == 0) mp.put(nums2[i], -1);
            else mp.put(nums2[i], stk[tt]);
            stk[++ tt] = nums2[i];
        }
        int[] res = new int[n];
        for(int i = 0; i < n; i ++){
            res[i] = mp.get(nums1[i]);
        }
        return res;
    }
}

3.下一个更大元素II

题目链接:503. 下一个更大元素 II - 力扣(LeetCode)

思路:从右往左遍历 实现单调递减栈,记录当前每一个数 右边第一个大于它的数的位置,与上一题不同的是,本题的数组是环形的。
我们可以将原数组扩展为两倍得长度 就相当于我们进行了下一个更大元素I的两次操作 —— (将循环数组变为链状)(实在不理解就手动模拟就好了)

1 2 1 —> 1 2 1 1 2 1

将环变为链,相当于我们可以考虑到当前数的左边的数(也就是当前数在环中的向右的下一个元素(其实就是在它的左边))

Code

class Solution {

     int N = 100010;
     int[] stk = new int[N];
     int tt;
    public int[] nextGreaterElements(int[] a) {
        int n = a.length;
        int[] res = new int[n];
        int cnt = 0;
        for(int i = 2 * n - 1; i >= 0; i --){
            int x = a[i % n];
            int idx = i % n;

            while(tt != 0 && stk[tt] <= x) tt --;
            if(tt == 0) res[idx] = -1;
            else res[idx] = stk[tt];// 记录右边大于它的第一个数的位置
            
            stk[++ tt] = x;// 入栈
        }
        
        return res;
    }
}

4. 接雨水

题目链接:

Code

思路一:动态规划

在这里插入图片描述

状态表示:

  • left[i]:表示柱子i左侧的最高柱子高度为left[i]
  • right[i]:表示柱子i右侧的最高柱子高度为right[i]

状态计算:

  • left[i] = max(left[i - 1], heigth[i - 1])
  • right[i] = max(right[i + 1], heigth[i + 1])

初始化:

  • left[0] = 0

  • rigth[n - 1] = 0

Code

class Solution {
    public int trap(int[] height) {
        int n = height.length;
        int[] left = new int[n + 10];
        int[] right = new int[n + 10];

        // 初始化
        // Arrays.fill(left, 0);
        // Arrays.fill(right, 0);
        left[0] = 0;
        right[n - 1] = 0;

        // 求每根柱子左右两边的最大高度
        for(int i = 1; i < n; i ++){
            left[i] = Math.max(left[i - 1], height[i - 1]);// 有点DP的意味
        }
        for(int i = n - 2; i >= 0; i --){
            right[i] = Math.max(right[i + 1], height[i + 1]);
        }

        // 求雨水面积
        int res = 0;
        for(int i = 0; i < n; i ++){// 计算每一个位置可以接收的水量
            int minH = Math.min(left[i], right[i]);
            if(minH > height[i]){
                res += (minH - height[i]);
            }    
        }
        return res;
    }
}

这种解法的积水量是一列一列求取的!

思路二:单调栈

单调递减栈

  • 理解题目,参考图,注意题目的性质,当后面的柱子高度比前面的低时,是无法接雨水的
  • 当找到一根比前面高的柱子,就可以计算接到的雨水,所以使用单调递减

这种解法面积是横向求取的!

Code

/**
    单调栈(递减栈):当遇到大于栈顶元素的柱子(说明可以形成水洼),然后再获取当前洼的高度(栈顶元素就是当前元素)和左边最低的高度(栈顶元素的下一个元素(大于等于当前高度)),即可计算面积(横向的): res += h(min(L, R) - heigth[cur]) * w,否则元素下标入栈
 */
class Solution {
    int[] stk = new int[2 * 10000 + 10];
    int tt = 0;
    public int trap(int[] height) {
        int n = height.length;
        int res = 0;
        for(int i = 0; i < n; i ++){
            while(tt != 0 && height[stk[tt]] < height[i]){
                int cur = stk[tt --];
                if(tt == 0){// 栈为空时 就break
                    break;
                }
                int l = stk[tt];
                int r = i;
                int h = Math.min(height[l], height[r]) - height[cur];
                res += h * (r - l - 1);
            }
            stk[++ tt] = i;
        }
        return res;
    }
}

5.柱状图中最大的矩形

题目链接:84. 柱状图中最大的矩形 - 力扣(LeetCode)

对于每一个位置我们怎么求它所能构成的最大矩阵呢?首先,要想找到第 i 位置最大面积是什么?

是以i 为中心(矩形的高度height[i]决定),要使得面积最大,那么就要找到最大宽度,如何寻找最大宽度是关键:

  • 向左第一个小于 heights[i] 的位置 left_i(left_i后面的位置到i((left_i,i])都是可以参与以heigth[i]为高度矩形的构成);

  • 向右第一个小于于 heights[i] 的位置 right_i( right_i前面的位置到([i, right_i))i都是可以参与以heigth[i]为高度矩形的构成)

即最大面积为 heights[i] * (right_i - left_i -1)

Code

/**
    思路:单调栈
    去求每一个位置i 左右两边第一个小于当前i位置高度的位置, 当前位置的最大矩形 s = (R-L-1) * h[i]
    记录两个特殊临界值:
        当找左边时,栈为空(说明无小于它的,都可以取,我们将其位置即为-1 l[i] = -1)
        当找右边时,栈为空(说明无小于它的,都可以取,我们将其位置即为n r[i] = n)
    我们开两个数组记录每一个位置 左右两边第一个小于它的位置,然后枚举计算最大值即可
    
 */

 /**
    单调递增栈:求左/右边第一个小于它的数的位置(顺序/逆序遍历)
  */

class Solution {
    int[] stk = new int[100010];
    int tt = 0;
    int[] l = new int[100010];
    int[] r = new int[100010];
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;

        for(int i = 0; i < n; i ++){
            while(tt != 0 && heights[stk[tt]] >= heights[i]) tt --;
            if(tt == 0) l[i] = -1;// 我们认为zuo边没有最小时指定为位置-1
            else l[i] = stk[tt];
            stk[++ tt] = i;// 记录左边第一个小于它的位置
        }

        Arrays.fill(stk, 0);
        tt = 0;
        for(int i = n - 1; i >= 0; i --){
            while(tt != 0 && heights[stk[tt]] >= heights[i]) tt --;
            if(tt == 0) r[i] = n;// 我们认为右边没有最小时指定为位置n
            else r[i] = stk[tt];
            stk[++ tt] = i;
        }
        // for(int i = 0; i < n; i ++){
        //     System.out.print(l[i] + " ");
        // }
        // System.out.println();
        // for(int i = 0; i < n; i ++){
        //     System.out.print(r[i] + " ");
        // }        
        int res = 0;
        for(int i = 0; i < n; i ++){
            res = Math.max(res, (r[i] - l[i] - 1) * heights[i]);
        }
        return res;
    }
}

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

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

相关文章

倒排索引-字符串相似匹配(结巴分词、中文转拼音)

工作中&#xff0c;遇到有两个不同的系统&#xff0c;两个系统中有相同的功能&#xff0c;维护一个主播的名称。现在准备将两个系统的主播合并到一起。因为主播名称可能由不同的人维护的&#xff0c;他们也不知道主播的真实姓名&#xff0c;比如一条小团团&#xff0c;可能维护…

香橙派3LTS部署ROS2阿克曼开源平台

1.系统镜像下载 这里我们需要安装ROS2的humble版本&#xff0c;需要ubuntu 22.04版本的系统。 香橙派镜像下载&#xff1a;http://www.orangepi.cn/html/hardWare/computerAndMicrocontrollers/service-and-support/Orange-Pi-3-LTS.html 点击Ubuntu镜像&#xff0c;选择jamm…

【linux】进程概念详述

进程概念一、冯诺依曼系统二、操作系统2.1 OS层次图2.2 操作系统的意义2.2.1 系统调用与库函数的区别2.3 管理的理解三、进程3.1 进程的概念3.2 描述进程-PCB3.3 进程和程序3.4 PCB内容3.4.1 查看进程3.4.2 标识符3.4.3 状态3.4.4 程序计数器3.4.5 记账信息3.4.6 上下文信息❗️…

1.极限与连续-——“机器学习中的数学”

1.通过集合相等来讲解什么是映射关系 上面问的这个问题&#xff08;2N N2&#xff09;说明了什么&#xff1f; ——两个无穷集合&#xff0c;如果能找到一种对应关系&#xff08;映射关系&#xff09;&#xff0c;那么我们就可以说这两个集合是等价的。 数列的极限就是趋势 …

字符串转二叉树

一. 题目介绍 二. 题目分析 首先 题目让我们以先序遍历的方式用字符串建立一个二叉树 输入是一个字符串 输出是是以中序遍历二叉树打印 我们先来看最简单的输入 这里只要建立一个字符数组 然后等测试用例输入就好了 // 接受输入值char arr[100]{0};scanf("%s",…

网络原理 --- 传输层Ⅳ TCP协议中的延迟应答、捎带应答、面向字节流、TCP中的异常处理

文章目录网络原理传输层TCP协议7.延迟应答8.捎带应答9. 面向字节流10.TCP中的异常处理总结网络原理 介绍TCP/IP协议中每一层里面的核心内容~ 应用层传输层网络层数据链路层物理层 传输层TCP协议 7.延迟应答 提高传输效率的机制 又是基于流量控制,来引入的提高效率的机制 实…

数据在内存中的存储

目录 数据在内存中的存储&#xff1a;&#xff1a; 整型及其浮点型存储方式: 1.数据类型介绍 2.整形在内存中的存储&#xff1a;原码 反码 补码 3.大小端字节序介绍及判断 4.浮点型在内存中的存储 5.两道经典指针选择题 C语言编程训练(牛客网) 1.喝汽水问题 2.字符串逆序 3.打…

Python编程运算符 ——算数运算符

作者简介&#xff1a;一名在校计算机学生、每天分享Python的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.运算符 1.在Python运算符中分为 2.算数运算符 3.比较重要的运算符 4…

网络原理——No.1 传输层_TCP的确认应答机制与超时重传

JavaEE传送门JavaEE JavaEE——网络原理_应用层 网络原理——传输层_UDP 目录传输层TCPTCP 的基本特性确认应答机制超时重传传输层 端到端之间的传输, 重点关注的是起点和终点 核心的协议有两个: UDP: 无连接, 不可靠传输,面向数据报, 全双工 TCP: 有链接, 可靠传输, 面向字…

黑猫带你学UFS协议第11篇:什么是逻辑单元(LU)与逻辑块(Sector)

本文依据UFS3.1 JEDEC协议及个人工作经验整理而成,如有错误请留言。 文章为个人辛苦整理,付费内容,已加入原创维权,禁止私自转载。 文章所在专栏:《黑猫带你学:UFS协议详解》 1 LU简介 我们对与UFS、emmc一类存储芯片,最重要的功能是什么?无非就是存数据和取出数据,也…

神经网络的输入稀疏矩阵,神经网络中的矩阵运算

1、BP神经网络模型各个参数的选取问题 样本变量不需要那么多&#xff0c;因为神经网络的信息存储能力有限&#xff0c;过多的样本会造成一些有用的信息被丢弃。如果样本数量过多&#xff0c;应增加隐层节点数或隐层数目&#xff0c;才能增强学习能力。 一、隐层数 一般认为&am…

牛客前端刷题(九)—— 打包篇

还在担心面试不通过吗&#xff1f;给大家推荐一个超级好用的刷面试题神器&#xff1a;牛客网&#xff0c;里面涵盖了各个领域的面试题库&#xff0c;还有大厂真题哦&#xff01; 赶快悄悄的努力起来吧&#xff0c;不苒在这里衷心祝愿各位大佬都能顺利通过面试。 面试专栏分享&a…

greenplum 源码解析 FTS辅助进程--ReadMe

简介 在greenplum数据库中master节点有一个专属进程Fault Tolerance Service (FTS: 容错服务)&#xff0c;其主要功能是检测所有segment节点的健康信息&#xff0c;如果其检测到segment节点的primary出现异常[硬件故障/宕机]&#xff0c;会在第一时间将其对应的mirror提升为新的…

MySql(50)MySQL日志

文章目录MySQL支持的日志日志类型日志的弊端慢查询日志(slow query log)通用查询日志查看当前状态启动日志永久启动临时启动查看日志删除\刷新日志错误日志(error log)删除\刷新日志二进制日志(bin log)查看默认情况日志参数设置查看日志使用日志恢复数据删除二进制日志PURGE M…

vue3 响应式对象的 api ,你全用过了吗?

文章目录Ⅰ. ref、reactive ( 递归监听 )Ⅱ. isRef、isReactive ( 判断 )Ⅲ. toRef 和 toRefs ( 解构 )Ⅳ. toRaw 、 markRaw ( 解除代理)Ⅴ. unref ( 拷贝 )Ⅵ. shallowRef 、shallowReactive&#xff08; 非递归监听 &#xff09;Ⅶ. triggerRef &#xff08;强制更新&#x…

红黑树原理及旋转

红黑树&#xff0c;本质上来说就是一棵二叉查找树 但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡 保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n) 但它是如何保证一棵n个结点的红黑树的高度始终保持在h logn的呢&#xff1f;这就引出了红黑…

相似度_对抗学习:SimCSE: Simple Contrastive Learning of Sentence Embeddings

SimCSE: Simple Contrastive Learning of Sentence Embeddings 这篇论文&#xff0c;我觉得有意思。在创造对抗学习的正负例时&#xff0c;正例直接使用它本身。将同一个句子传递给预先训练好的编码器两次&#xff1a;通过应用两次dropout&#xff0c;我们可以得到两个不同的嵌…

云计算基础:云计算越来越广泛,我们应该如何去学习云计算

随着时代的发展&#xff0c;云计算越来越普及&#xff0c;越大众化&#xff0c;使用的人越来越多&#xff0c;我们应该如何去学习这门技术呢?这篇文章我们就来介绍如何学习我们的云计算。 学前介绍&#xff1a; 学前介绍 学习资料&#xff1a;HedEX Lite、产品文档、笔记、P…

非科班程序员被裁员后反而涨薪了200%,这两个月他都经历了哪些?

今年年初开始&#xff0c;裁员潮一浪接着一浪翻滚而来&#xff0c;让互联网人胆颤心惊&#xff0c;时刻担心下一波裁员是否就要降临到自己身上。 小帅&#xff08;化名&#xff09;是一个原本月薪8k&#xff0c;在小外包公司做政府项目的一名普通员工&#xff0c;前不久就被裁员…

数据仓库建模(四):维度表的设计

数据仓库建模&#xff08;四&#xff09;&#xff1a;维度表的设计一、维度表的整体结构1.1 维度表的结构设计1.2 维度代理键1.3 自然键、超久键和超自然键1.4 下钻与上卷1.5 维度退化1.6 非规范化的扁平维度1.7 多层次维度1.8 维度属性的标识与状态信息1.9 维度表中的空值属性…