单调栈笔记

单调栈

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

单调栈正如其名字,用一个栈(能够实现栈性质的数据结构就行)来存储元素,存储在栈中的元素保持单调性(单调递增或者是单调递减)单调常用来解决寻找任一个元素左边或者右边第一个比自己小或者大的元素位置,大家可以想象看,如果是只需要找某一个元素的话,我们遍历一次数组就好了,但是如果是找数组中所有元素的左边或者右边第一个比自己小或者大的元素位置时,那么每找一次遍历一次数组的话,这样会是O(n^2)的时间复杂度,单调栈就是来实现一次遍历解决问题的办法。

具体如何实现,以及单调栈如何维护的,请看下面的题目

1.每日温度

在这里插入图片描述这个题目是单调栈的经典应用,我们需要找到下一个比当前天气温高的日子出现在几天之后。

1.单调栈中存储的是数组下标index
2.单调栈中:栈顶到栈底保持单调递减

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        vector<int> ans(temperatures.size(), 0);
        //定义一个单调栈
        vector<int> stack;
        for(int i = 0; i < temperatures.size(); i++){
            if(stack.size() == 0 || temperatures[stack.back()] >= temperatures[i]){
                stack.push_back(i);
            }else{
                //将单调栈中所有小于当前元素的栈针弹出来(保持单调性)
                while(stack.size() > 0 && temperatures[stack.back()] < temperatures[i]){
                    int index = stack.back();
                    stack.pop_back();
                    ans[index] = i - index; 
                }
                //将当前元素添加到栈中
                stack.push_back(i);
            }
        }
        return ans;
    }
};

2.下一个更大元素 I

在这里插入图片描述
这个题目可以利用我们普通暴力解法(多层循环)实现,但是下面给出的是单调栈的实现

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        vector<int> stack;//单调栈
        unordered_map<int, int> m; //存储nums2中 nums2[i]和i的键值对
        vector<int> first_max_index(nums2.size(), -1);
        for(int i = 0; i < nums2.size(); i++){
            //先将nums2[i]和i的键值对存储好
            m[nums2[i]] = i;
            //开始单调栈的过程
            if(stack.empty() || nums2[stack.back()] >= nums2[i]){
                stack.push_back(i);
            }else{
                while(!stack.empty() && nums2[stack.back()] < nums2[i]){
                    int index = stack.back();
                    stack.pop_back();
                    first_max_index[index] = i;
                }
                stack.push_back(i);
            }
        }
        vector<int> ans(nums1.size(), -1);
        for(int i = 0; i < nums1.size(); i++){
            //nums1[i]在nums2中下标
            int index = m[nums1[i]];
            //获得nums2[index]右侧第一个大于的元素
            int first_index = first_max_index[index];
            if(first_index != -1) ans[i] = nums2[first_index];
        }
        return ans;
    }
};

3.下一个更大的元素

在这里插入图片描述这个题目在上一个题目的基础上,将数组收尾相连了起来,而且数组长度也变大了,所以就不能够通过暴力求解了,只能够通过单调栈来实现。

思路:直接在原数组再拼接一个原数组(解决首尾相连),然后通过单调栈来找下一个更大元素

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        int n = nums.size();
        //将循环数组给铺平
        for(int i = 0; i < n; i++){
            nums.push_back(nums[i]);
        }
        vector<int> ans(n * 2, -1);
        vector<int> stack;
        for(int i = 0; i < 2 * n; i++){
            if(stack.empty() || nums[stack.back()] >= nums[i]){
                stack.push_back(i);
            }else{
                while(!stack.empty() && nums[stack.back()] < nums[i]){
                    int index = stack.back();
                    stack.pop_back();
                    ans[index] = nums[i];
                }
                stack.push_back(i);
            }
        }
        for(int i = 0; i < n; i++){
            ans.pop_back();
        }
        // 写法二:最后再把结果集即ans数组resize到原数组大小
        //ans.resize(nums.size() / 2);
        return ans;
    }
};

方法优化,不通过拼接解决收尾相连

// 版本二
class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        vector<int> result(nums.size(), -1);
        if (nums.size() == 0) return result;
        stack<int> st;
        for (int i = 0; i < nums.size() * 2; i++) {
            // 模拟遍历两边nums,注意一下都是用i % nums.size()来操作
            while (!st.empty() && nums[i % nums.size()] > nums[st.top()]) {
                result[st.top()] = nums[i % nums.size()];
                st.pop();
            }
            st.push(i % nums.size());
        }
        return result;
    }
};

4.接雨水

在这里插入图片描述
这个题目常见的有两种实现方案,一种是双指针实现,另外一种是单调栈实现,这个两种方法实现的区别就在于前者是按照列为主来计算面积的,后者是以行为主来计算面积。

双指针:通过前缀数组和后缀数组实现寻在某列两侧高度最大的列

在这里插入图片描述

# python实现代码,c++实现逻辑也是一样的
class Solution:
    def trap(self, height: List[int]) -> int:
        n = len(height)
        prefix, suffix = [0] * n, [0] * n
        # 前缀最大值
        prefix[0] = height[0]
        for i in range(1, n):
            prefix[i] = max(prefix[i - 1], height[i])
        # 后缀最大值
        suffix[-1] = height[-1]
        for i in range(n - 2, -1, -1):
            suffix[i] = max(suffix[i + 1], height[i])
        # 每一个格子对应能解多少水等于前后缀最大值的中的最小值 - 柱子高度
        ans = 0
        for h, pre, suf in zip(height, prefix, suffix):
            ans += min(pre, suf) - h
        return ans

单调栈:单调栈的计算是将每行的面积累加完成的

在这里插入图片描述

class Solution {
public:
    //单调栈实现
    int trap(vector<int>& height) {
        stack<int> st;
        st.push(0);
        int sum = 0;
        for(int i = 0; i < height.size(); i++){
            while(!st.empty() && height[i] > height[st.top()]){
                int mid = st.top();
                st.pop();
                if(!st.empty()){
                    int h = min(height[st.top()], height[i]) - height[mid];
                    int w = i - st.top() - 1;
                    sum += h * w;
                }
            }
            st.push(i);
        }
        return sum;
    }
};

5.柱状图中最大的矩形

在这里插入图片描述本次的解题思路和上题接雨水是一样的,但是本题中,我们需要关注的是矮的柱子(短板原理),上一题是关注高的柱子。同上有两种解法,下面给出的是单调栈的解法。(这个题目比上个题目要难一点,请注意如何处理收尾使得遍历逻辑连贯)

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        stack<int> st;
        //首部加入0是为了能够在某升序数组的情况下获得左边界;尾部加入0是为了能够方便循环的进行(能够将所有元素弹出计算)
        heights.insert(heights.begin(), 0);//数组头部加入元素0
        heights.insert(heights.end(), 0);//数组尾部加入元素0
        st.push(0);
        int result = 0;
        for(int i = 1; i < heights.size(); i++){
            while(heights[i] < heights[st.top()]){
                int mid = st.top();
                st.pop();
                int w = i - st.top() - 1;
                int h = heights[mid];
                result = max(result, w * h);
            }
            st.push(i);
        }
        return result;
    }
};

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

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

相关文章

Allegro如何设置飞线的显示方式?

预拉线最短化。 设置方法:选择菜单栏Setup→Design Parameters...(参数设置) 跳出下面对话框,根据需求设置。 Jogged:拼合的。 Straight:直的。 Closest endpoint:最靠近端点。 Pin to pin:引脚到引脚。 Jogged的显示效果。

MyBatis的逆向工程的创建,generator插件的使用和可能出现的一些问题,生成的实体类多出.java 1 .java 2这种拓展文件的处理方案

目录 创建逆向工程的步骤 ①添加依赖和插件 ②创建MyBatis的核心配置文件 ③创建逆向工程的配置文件 ④执行MBG插件的generate目标 数据库版本8有可能出现的问题&#xff1a; 1、生成的实体类多了.java 1 .java 2的拓展文件... 2、生成的属性与表中字段不匹配&#xff…

【IEEE会议征稿】2024年第九届智能计算与信号处理国际学术会议(ICSP 2024)

2024年第九届智能计算与信号处理国际学术会议&#xff08;ICSP 2024&#xff09; 2024年第八届智能计算与信号处理国际学术会议&#xff08;ICSP 2024&#xff09;将在西安举行&#xff0c; 会期是2024年4月19-21日&#xff0c; 为期三天, 会议由西安科技大学主办。 欢迎参会&…

Linux 一键部署influxd2-telegraf

influxd2前言 influxd2 是 InfluxDB 2.x 版本的后台进程,是一个开源的时序数据库平台,用于存储、查询和可视化时间序列数据。它提供了一个强大的查询语言和 API,可以快速而轻松地处理大量的高性能时序数据。 telegraf 是一个开源的代理程序,它可以收集、处理和传输各种不…

全双工通信协议:WebSockets+STOMP

全双工通信协议&#xff1a;WebSocketsSTOMP 前言启动STOMPWebSocket传输消息流注释控制器发送消息代理点作为分隔符证明用户目的地消息的顺序事件拦截STOMP客户端表演监视测试案例一&#xff1a;发送指定用户消息 关联文章 前言 WebSocket协议定义了两种类型的消息(文本和二进…

如何从 Android SD 卡恢复已删除的照片

您是否不小心从 Android SD 卡中删除了一些照片&#xff1f;您是否尝试访问昨天拍摄的照片&#xff0c;但无论您在哪里查看都找不到它们&#xff1f;您的 Android 手机的外部存储是否已损坏&#xff0c;其内容无法访问&#xff1f; 在这种情况下&#xff0c;您应该尽快采取行动…

Cmake(4)——库的创建和链接

库的创建和链接 插播&#xff01;插播&#xff01;插播&#xff01;亲爱的朋友们&#xff0c;我们的Cmake/Makefile/Shell这三个课程上线啦&#xff01;感兴趣的小伙伴可以去下面的链接学习哦~ 构建工具大师-CSDN程序员研修院 在众多成熟的项目中&#xff0c;有时会遇到这样…

【Spring源码分析】从源码角度去熟悉依赖注入(二)

从源码角度去熟悉依赖注入&#xff08;二&#xff09; 一、AutowiredFieldElement 注入分析二、AutowiredMethodElement注入分析三、doResolveDependency 源码分析1. Value 注解解析测试 ${} 和 #{} 2. resolveMultipleBeans 筛选特殊类型&#xff08;处理多Bean&#xff09;测…

关于网络协议的笔记

简介&#xff1a; 协议&#xff0c; 网络协议的简称&#xff0c;网络协议是通信计算机双方必须共同遵从的一组约定。如怎么样建立连 接、怎么样互相识别等。只有遵守这个约定&#xff0c;计算机之间才能相互通信交流。它的 三要素是&#xff1a;语 法、语义、时序。 为了使数…

水经微图系列产品新功能盘点!

水经微图&#xff0c;简称“微图”。 我们曾在直播中分享过微图APP苹果版的功能&#xff0c;本周四晚19:30我们将在另一个视频号分享盘点微图APP安卓版的详细功能&#xff0c;以及Web版近期上线的新功能功能。 微图APP安卓版 我们在《水经微图安卓版APP正式上线》一文中&…

configure: error: openSSL library not found.解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

初识node.js(使用)

文章目录 项目目录介绍和运行流程1.index.html&#x1f447;2.整个项目的核心入口文件其实是main.js3.App.vue 组件化开发 和 根组件普通组件的注册1.局部注册2.全局注册 综合案例 项目目录介绍和运行流程 1.index.html&#x1f447; <!DOCTYPE html> <html lang&quo…

Offer必备算法_滑动窗口_八道力扣OJ题详解(由浅到深)

目录 滑动窗口算法介绍 ①力扣209. 长度最小的子数组 解析及代码 ②力扣3. 无重复字符的最长子串 解析及代码 ③力扣1004. 最大连续1的个数 III 解析及代码 ④力扣1658. 将x减到0的最小操作数 解析及代码 ⑤力扣904. 水果成篮 解析及代码1&#xff08;使用容器&…

初识SQL注入

目录 注入攻击 SQL注入 手工注入 Information_schema数据库 自动注入 介绍一下这款工具&#xff1a;sqlmap 半自动注入 前面给大家通过学习练习的方式将XSS攻击的几种形式和一些简单的靶场和例题的演示&#xff0c;从本篇开始我将和小伙伴们通过边复习、边练习的方式来进…

2024年购买阿里云服务器多少钱?1000元左右预算可购买的8款云服务器参考

1000元左右预算可以买到哪些配置的阿里云服务器&#xff1f;目前阿里云活动中价格在1000元左右的云服务器有8款&#xff0c;其中经济型e实例云服务器三款&#xff0c;通用算力型u1实例云服务器五款&#xff0c;碰到阿里云有优惠券或者代金券活动时&#xff0c;购买过程中还能使…

Angular组件(一) 分割面板ShrinkSplitter

Angular组件(一) 分割面板ShrinkSplitter 前言 分割面板在日常开发中经常使用&#xff0c;可将一片区域&#xff0c;分割为可以拖拽整宽度或高度的两部分区域。模仿iview的分割面板组件&#xff0c;用angular实现该功能&#xff0c;支持拖拽和[(ngModel)]双向绑定的方式控制区…

Dock的安装部署和基础命令

1 Docker基础 1.1 Docker概述 Docker是一个开源的应用容器引擎&#xff0c;用来运行容器里的运用&#xff0c;可以用来管理容器和镜像的一种工具&#xff0c;基于go语言开发并遵循了apache2.0协议开源。 Docker是在Linux容器里运行应用的开源工具&#xff0c;是一种轻量级的…

Java(TM) Platform SE binary (Process Id: 4412)

Java™ Platform SE binary (Process Id: 4412&#xff09;JAVA8安装过程中出现上述问题win10解决方法 打开任务管理器 在任务管理器中找到详细信息&#xff0c;然后根据上边的进程id找到对应的程序&#xff0c;右键结束任务即可。 在安装jdk17时候&#xff0c;同时出现了上…

05 双向链表

目录 1.双向链表 2.实现 3.OJ题 4.链表和顺序表对比 1. 双向链表 前面写了单向链表&#xff0c;复习一下 无头单向非循环链表&#xff1a;结构简单&#xff0c;一般不会单独用来存数据。实际中更多作为其他数据结构的子结构&#xff0c;如哈希桶、图的邻接等。另外这种结构在…

Pycharm中出现Comparison with None performed with equality operators

此图中警告翻译过来是 &#xff1a;与使用相等运算符执行的None进行比较 这里不应该使用 或者 &#xff01; 而应改为 is 或者 is not
最新文章