单调栈笔记

news2024/11/22 9:26:28

单调栈

  • 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.coloradmin.cn/o/1406317.html

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

相关文章

SE通道注意力机制模块

简介 论文原址&#xff1a;https://arxiv.org/pdf/1709.01507.pdf 在深度学习领域&#xff0c;提升模型的表征能力一直是一个关键的研究方向。SE&#xff08;Squeeze-and-Excitation&#xff09;模块是一种引入通道注意力机制的方法&#xff0c;旨在让神经网络更加关注对当前…

5_机械臂运动学基础_矩阵

上次说的向量空间是为矩阵服务的。 1、学科回顾 从科技实践中来的数学问题无非分为两类&#xff1a;一类是线性问题&#xff0c;一类是非线性问题。线性问题是研究最久、理论最完善的&#xff1b;而非线性问题则可以在一定基础上转化为线性问题求解。 线性变换&#xff1a; 数域…

活动回顾丨云原生技术实践营上海站「云原生 AI 大数据」专场(附 PPT)

AI 势不可挡&#xff0c;“智算”赋能未来。2024 年 1 月 5 日&#xff0c;云原生技术实践营「云原生 AI &大数据」专场在上海落幕。活动聚焦容器、可观测、微服务产品技术领域&#xff0c;以云原生 AI 工程化落地为主要方向&#xff0c;希望帮助企业和开发者更快、更高效地…

机器学习期末复习总结笔记(李航统计学习方法)

文章目录 模型复杂度高---过拟合分类与回归有监督、无监督、半监督正则化生成模型和判别模型感知机KNN朴素贝叶斯决策树SVMAdaboost聚类风险PCA深度学习范数计算梯度下降与随机梯度下降SGD线性回归逻辑回归最大熵模型适用性讨论 模型复杂度高—过拟合 是什么&#xff1a;当模型…

应用监控 eBPF 版:实现高效协议解析的技术探索

作者&#xff1a;彦鸿 引言 随着 Kuberentes 等云原生技术的飞速发展&#xff0c;带来了研发与运维模式的变革。企业软件架构由单体服务向分布式、微服务演进。随着业务发展&#xff0c;多语言、多框架、多协议的微服务在企业中越来越多&#xff0c;软件架构复杂度越来越高&a…

excel(wps)之vlookup函数合并sheet数据

VLOOKUP函数是Excel中的一个纵向查找函数&#xff0c;它与LOOKUP函数和HLOOKUP函数属于一类函数&#xff0c;在工作中都有广泛应用&#xff0c;例如可以用来核对数据&#xff0c;多个表格之间快速导入数据等函数功能。功能是按列查找&#xff0c;最终返回该列所需查询列序所对应…

C++并发编程 -1.线程管理

本栏主要介绍《C并发实战编程》这本书&#xff0c;链接。 将按照书目录分为 九章节介绍C并发编程&#xff0c;尽可能简化本书内晦涩难懂知识点。 本章节主要讲解线程使用基础。详细介绍线程使用基础&#xff08;线程发起、等待、参数、异常处理&#xff09;、线程管控(归属权、…

设计模式——1_6 代理(Proxy)

诗有可解不可解&#xff0c;若镜花水月勿泥其迹可也 —— 谢榛 文章目录 定义图纸一个例子&#xff1a;图片搜索器图片加载搜索器直接在Image添加组合他们 各种各样的代理远程代理&#xff1a;镜中月&#xff0c;水中花保护代理&#xff1a;对象也该有隐私引用代理&#xff1a;…

uniapp点击事件报错 Cannot read property ‘stopPropagation‘ of undefined

问题产生&#xff1a;在列表上有个小按钮&#xff0c;可点击弹出选择框。 列表本身可点击进入详情页。所以想用click.stop来阻止点击小按钮时候&#xff0c;触发列表的点击事件。 结果&#xff1a;如图所示 解决方案&#xff1a;发现自己用的是icon&#xff0c;在icon上加click…

Web3 游戏开发者的数据分析指南

作者&#xff1a;lesleyfootprint.network 在竞争激烈的 Web3 游戏行业中&#xff0c;成功不仅仅取决于游戏的发布&#xff0c;还需要在游戏运营过程中有高度的敏锐性&#xff0c;以应对下一次牛市的来临。 人们对 2024 年的游戏行业充满信心。A16Z GAMES 和 GAMES FUND ONE …

windows和linux下SHA1,MD5,SHA256校验办法

今天更新android studio到Android Studio Hedgehog | 2023.1.1时&#xff0c;发现提示本机安装的git版本太老&#xff0c;于是从git官网下载最新的git。 git下载地址&#xff1a; https://git-scm.com/ 从官网点击下载最新windows版本会跳转到github仓库来下载发布的git&…

uniapp小程序实现自定义返回按钮和胶囊对齐 做到兼容各手机型号

效果&#xff1a; 用到的API&#xff1a; uni.getMenuButtonBoundingClientRect();官网地址&#xff1a; https://uniapp.dcloud.net.cn/api/ui/menuButton.html#getmenubuttonboundingclientrect 控制台打印&#xff1a; 代码示例&#xff1a; <template><view cl…

惬意上手python —— JSON模块介绍

JSON模块简介 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;易于阅读和编写&#xff0c;同时也易于机器解析和生成。在Python中&#xff0c;我们可以使用json模块来处理JSON数据。本文将介绍如何使用Python的json模块进行JS…

Redis解决方案:NOAUTH Authentication required(连接jedis绑定密码或修改redis密码)

Redis解决方案&#xff1a;NOAUTH Authentication required&#xff08;连接jedis绑定密码或修改redis密码&#xff09; Java使用jedis连接redis时出现错误NOAUTH Authentication required 一、问题报错和原因 本地设置了redis的密码&#xff0c;但在远程连接时并没有输入密…

【深度学习:数据管理工具】2024 年计算机视觉的 7 大数据管理工具

【深度学习&#xff1a;数据管理工具】2024 年计算机视觉的 7 大数据管理工具 什么是计算机视觉中的数据管理&#xff1f;在计算机视觉中的数据管理工具中要考虑什么&#xff1f;Data Prioritization 数据优先级Visualizations 可视 化Model-Assisted Insights 模型辅助见解Mod…

解锁加密货币增长的秘密:通过 Token Explorer 解读市场信号

解读市场信号&#xff0c;就像医生通过观察患者的体征来判断健康状况一样&#xff0c;可以帮助我们评估加密货币的采用速度。 Token Explorer 这个工具&#xff0c;就像是我们医生的听诊器&#xff0c;它追踪了一些核心的采用指标&#xff1a; ● 市值&#xff1a;通过比较主…

【c++学习】数据结构中的链表

c链表 数据结构中的链表代码 数据结构中的链表 链表与线性表相对&#xff0c;链表数据在内存中的存储空间是不连续的&#xff0c;链表每个节点包含数据域和指针域。 代码 下述代码实现了链表及其接口 包括增、删、查、改以及其他一些简单的功能 #include <iostream>u…

对Vue有状态组件和无状态组件的理解及使用场景

目录 一、Vue框架 二、Vue的有状态组件 三、Vue的无状态组件 四、有状态组件和无状态组件的区别 一、Vue框架 Vue是一款流行的JavaScript框架&#xff0c;用于构建用户界面。它被设计为易学易用的&#xff0c;同时也具备强大的功能和灵活性。 Vue具有以下特点&#xff1a…

AI智能分析网关V4车辆检测算法及车辆结构化数据在车辆智能管控中的应用

AI边缘计算智能分析网关V4车辆检测、车牌识别算法融合了ORC识别、云计算、计算机视觉、大数据检索等多种技术&#xff0c;可将运动中的机动车牌照从复杂的背景中提取并识别出来&#xff0c;通过车牌提取、图像预处理、特征提取、车牌字符识别等流程&#xff0c;识别出车辆牌号、…

鸿蒙开发实战-OpenHarmony之天气应用

“天气之子” 功能描述&#xff1a; 通过请求免费API获取指定城市七天内相关天气信息 开发环境&#xff1a; IDE:DEV ECO 4.0.600 SDK&#xff1a;4.0.10.15 开发板:DAYU200 4.0.10.16 开发过程 一. 创建项目&#xff0c;调试环境 1.创建项目 2.选择OpenHarmony、API1…