Studying-代码随想录训练营day49| 42. 接雨水、84.柱状图中最大的矩形

news2024/11/14 21:09:06

第49天,单调栈part02,两个很经典的例题,编程语言:C++

目录

42. 接雨水

84.柱状图中最大的矩形

总结: 


42. 接雨水

文档讲解:代码随想录接雨水

视频讲解:手撕接雨水

题目: 42. 接雨水 - 力扣(LeetCode)

学习:本题是非常巧妙的一道题,首先第一个重点是要理解题意,凹槽是存在左边比自己高,右边也比自己高的位置。本题有两个计算思路:按行计算和按列计算。

按照行计算:

按照列计算:

1.我们首先考虑按列计算的方法:按照列来计算的话,宽度一定是1了,我们再把每一列的雨水的高度求出来就可以了。(要注意第一个柱子和最后一个柱子是不接雨水的)

找到每个柱子左边和右边的最高柱子有两个办法:

①暴力求解:通过一个for循环,遍历当前柱子,两个for循环找到左边和右边最高的柱子,最后选取最小的柱子即可。

//时间复杂度O(n^2)
//空间复杂度O(1)
class Solution {
public:
    int trap(vector<int>& height) {
        int sum = 0;
        for (int i = 0; i < height.size(); i++) {
            // 第一个柱子和最后一个柱子不接雨水
            if (i == 0 || i == height.size() - 1) continue;

            int rHeight = height[i]; // 记录右边柱子的最高高度
            int lHeight = height[i]; // 记录左边柱子的最高高度
            for (int r = i + 1; r < height.size(); r++) {
                if (height[r] > rHeight) rHeight = height[r];
            }
            for (int l = i - 1; l >= 0; l--) {
                if (height[l] > lHeight) lHeight = height[l];
            }
            int h = min(lHeight, rHeight) - height[i];
            if (h > 0) sum += h;
        }
        return sum;
    }
};

②我们也可以采用双指针的方法,提前预处理好每个点左右最高的柱子,然后进行求解,能够降低时间复杂度。

//时间复杂度O(n)3n
//空间复杂度O(n)
class Solution {
public:
    int trap(vector<int>& height) {
        if (height.size() <= 2) return 0;
        vector<int> maxLeft(height.size(), 0);
        vector<int> maxRight(height.size(), 0);
        int size = maxRight.size();

        // 记录每个柱子左边柱子最大高度
        maxLeft[0] = height[0];
        for (int i = 1; i < size; i++) {
            maxLeft[i] = max(height[i], maxLeft[i - 1]);
        }
        // 记录每个柱子右边柱子最大高度
        maxRight[size - 1] = height[size - 1];
        for (int i = size - 2; i >= 0; i--) {
            maxRight[i] = max(height[i], maxRight[i + 1]);
        }
        // 求和
        int sum = 0;
        for (int i = 0; i < size; i++) {
            int count = min(maxLeft[i], maxRight[i]) - height[i];
            if (count > 0) sum += count;
        }
        return sum;
    }
};

2.除此之外,我们还可以考虑行计算的方式,采用的是单调栈的方法。

基于行计算,我们可以发现,如果从栈顶到栈底是从小到大的排列顺序的话。那么一旦发现添加的柱子高度大于栈头元素了,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。下图给一个例子:

假如遇到相同的柱子,我们肯定需要把当前柱子加入栈中,前一个值相同的柱子可以选择去掉也可可以原则保留,对结果没有影响。因为我们计算的时候,左边柱子如果和本身一样的话,实际上最后求得的高度是0。

单调栈处理逻辑:

  • 情况一:当前遍历的元素(柱子)高度小于栈顶元素的高度 height[i] < height[st.top()]
  • 情况二:当前遍历的元素(柱子)高度等于栈顶元素的高度 height[i] == height[st.top()]
  • 情况三:当前遍历的元素(柱子)高度大于栈顶元素的高度 height[i] > height[st.top()]

我们需要针对这三种情况进行处理:

代码:

//时间复杂度O(n)
//空间复杂度O(n)
class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        if(heights.size() == 1) return heights[0];
        int result = 0; //记录结果
        stack<int> st; //单调栈,加入的是已经遍历的元素
        //我们需要首尾加0或者-1来完成首尾元素的遍历
        heights.insert(heights.begin(), 0); //头部加0
        heights.push_back(0); //尾部加0

        //我们需要找多左右第一个小于当前的值
        st.push(0);
        for(int i = 1; i < heights.size(); i++) {
            if(heights[i] > heights[st.top()]) { //从栈顶到栈底是递减的
                st.push(i);
            }
            else if(heights[i] == heights[st.top()]) {
                st.pop(); //也可以不加这个,只是要多计算一次0
                st.push(i);
            }
            else {
                while(!st.empty() && heights[i] < heights[st.top()]) { //此处需要循环,不断找到合适的数值
                    int mid = st.top();
                    st.pop();
                    if(!st.empty()) {
                        int h = heights[mid];
                        int w = i - st.top() - 1;
                        result = max(result, h * w);
                    }
                } 
                st.push(i); //注意每一次都需要把当前下标加入
            }
        }
        return result;
    }
};

84.柱状图中最大的矩形

文档讲解:代码随想录柱状图中最大的矩形

视频讲解:手撕柱状图中最大的矩形

题目:代码随想录柱状图中最大的矩形

学习:本题实际上和上一题是一样的,上一题是找左右最大值,而本题则是找左右的最小值。每一个柱都可以向左右扩展到比它小的柱的位置。因此本题和上一题的唯一区别就在于,本题找的是左右的最小值,而上一题找的是左右的最大值。

因此本题的暴力解法和双指针优化方法和上一题的逻辑基本相同,再次就不过多赘述,我们主要讲解单调栈的方法。

由于本题是求解左右的最小值,因此本题单调栈里元素的顺序,从栈顶到栈底应该是从大到小的顺序!可以发现栈顶和栈顶的下一个元素以及要入栈的三个元素组成了我们要求最大面积的高度和宽度。

因此单调栈的处理逻辑的顺序也需要发生改变:

  • 情况一:当前遍历的元素heights[i]大于栈顶元素heights[st.top()]的情况
  • 情况二:当前遍历的元素heights[i]等于栈顶元素heights[st.top()]的情况
  • 情况三:当前遍历的元素heights[i]小于栈顶元素heights[st.top()]的情况

本题还需要注意一点,是需要在头部和尾部加一个0,用来处理第一个柱形和最后一个柱形。 例如如果没有在末尾加0的话,针对[2,4,6,8]的情况,我们就没办法找到最大的矩形8。

头部加0也是如此,为了处理第一个元素,防止其没有左边界。这是与上一题不同的,因为上一题不需要考虑下标0的左端和最后一个元素的右端。最后可以得到代码:

//时间复杂度O(n)
//空间复杂度O(n)
class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        if(heights.size() == 1) return heights[0];
        int result = 0; //记录结果
        stack<int> st; //单调栈,加入的是已经遍历的元素
        //我们需要首尾加0或者-1来完成首尾元素的遍历
        heights.insert(heights.begin(), 0); //头部加0
        heights.push_back(0); //尾部加0

        //我们需要找多左右第一个小于当前的值
        st.push(0);
        for(int i = 1; i < heights.size(); i++) {
            if(heights[i] > heights[st.top()]) { //从栈顶到栈底是递减的
                st.push(i);
            }
            else if(heights[i] == heights[st.top()]) {
                st.pop(); //也可以不加这个,只是要多计算一次0
                st.push(i);
            }
            else {
                while(!st.empty() && heights[i] < heights[st.top()]) { //此处需要循环,不断找到合适的数值
                    int mid = st.top();
                    st.pop();
                    if(!st.empty()) {
                        int h = heights[mid];
                        int w = i - st.top() - 1;
                        result = max(result, h * w);
                    }
                } 
                st.push(i); //注意每一次都需要把当前下标加入
            }
        }
        return result;
    }
};

总结: 

牢记单调栈的作用:寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置。

牢记单调栈的处理逻辑有三部分:

  • 情况一:当前遍历的元素大于栈顶元素的情况
  • 情况二:当前遍历的元素等于栈顶元素的情况
  • 情况三:当前遍历的元素小于栈顶元素的情况

牢记以上两点,才能正确的写出单调栈。

其次更重要的是要理解题意,理解题目需要我们求解的到底是什么,多加练习,多加思考。 

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

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

相关文章

轻松上手Scikit-learn——评估模型性能(准确率、精确率、召回率、ROC和AUC)

轻松上手Scikit-learn——评估模型性能&#xff08;准确率、精确率、召回率、ROC和AUC&#xff09; 安装scikit——learn pip install -U scikit-learn常用模型性能评估指标 在开始介绍之前先规定几个表示&#xff1a; 模型预测正确的正样本&#xff0c;称为真正样本&#…

AI会带来新的就业岗位吗?

最近&#xff0c;百度的首席执行官Roy在世界人工智能大会&#xff08;WAIC&#xff09;上提出了一个观点。他表示&#xff0c;大家无需过于担心人工智能会导致大量失业&#xff0c;相反&#xff0c;人工智能会创造一批新的就业岗位。他特别提到了几个核心的典型代表&#xff0c…

visual Studio怎么设置背景图片

想在visual Studio设置一个自己喜欢的背景图片&#xff0c;效果如下&#xff1a; 废话不多说&#xff0c;以Vs2022为例&#xff0c;操作步骤如下&#xff1a; 1.打开“扩展”>“管理扩展”>“搜索ClaudiaIDe”安装 或者直接下载官方安装版https://kbuchi.gallerycdn.vs…

网安科班精选!爱荷华大学教授的网络安全零基础入门教程!

网络就像一把双刃剑&#xff0c;给我们的生活、交流、工作和发展带来了便利&#xff0c;但同时也给信息安全以及个人隐私带来了威胁。网络和信息安全问题不仅影响了网络的普及和应用&#xff0c;还关系到企国家、军队、企业的信息安全和社会的经济安全&#xff0c;让人又爱又恨…

用栈实现队列(双栈思路 + 代码实现)

题目 ①双栈大体思路 1.切入点 栈先进后出&#xff0c;队列先进先出。 两个栈&#xff0c;其中一个栈可以用于颠倒顺序。顺序就跟队列一样 2.明确双栈作用 1.第一个栈接收输入&#xff08;输入栈&#xff09; 2.第二个栈转换顺序&#xff08;输出栈&#xff09; 3.四个功…

学习记录——day23 多进程编程

目录 一、多进程引入 1.1、引入目的 1.2、进程的概念 1.3、进程的种类 1.4、进程号的概念 1.5、特殊进程 0号 1号 2号 孤儿 僵尸 1.6、进程的相关命令 1&#xff09;查看进程信息的命令&#xff1a;ps 跟不同的选项&#xff0c;执行不同的状态 2&…

springboot的轻量替代框架-Solon

Java之所以是广泛使用的编程语言&#xff0c;不仅仅因为其具有跨平台性、面向对象、可靠性&#xff0c;还有很重要的一点是强大的生态系统&#xff0c;spring家族的一系列框架&#xff0c;对Java的崛起有着不可忽视的作用。 学Java的不可能不知道Spring&#xff0c;今天给大家…

品致差分探头和泰克差分探头的优势和特点分析

品致差分探头和泰克差分探头各有其独特的优势和特点&#xff0c;选择哪个更好主要取决于具体的应用场景、测试需求以及预算等因素。以下是对两个品牌差分探头的详细比较&#xff1a; 品致差分探头优势与特点&#xff1a; 高精度测量&#xff1a;品致差分探头具有高精度的测量…

docker(一):Develop faster. Run anywhere.

前言 在进行微服务部署时&#xff0c;首先需要进行部署环境的搭建。目前&#xff0c;Docker 已经成为了微服务部署的主流解决方案之一。Docker 可以帮助我们更快地打包、测试以及部署应用程序&#xff0c;从而缩短从编写到部署运行代码的周期。 在本文中&#xff0c;我们将对…

ChatGPT的高级语音功能“Her”终于上线啦!!

ChatGPT版“Her”突然开放&#xff0c;第一批体验报告来了&#xff01;用户们已经疯狂 刚刚&#xff0c;万众期待的GPT-4o语音功能&#xff0c;也就是被称为“Her”的功能&#xff0c;终于开始向用户陆续开放&#xff01; OpenAI官方推特表示&#xff0c;选中的Alpha用户们将在…

快速部署私有化大模型 毕昇(使用docker-compose方式)

docker安装 1. # Linux系统安装docker&#xff0c;以CentOS/RHEL为例&#xff0c;其他操作系统请参考docker官方安装方法 # 如果已经安装过docker 期望重装&#xff0c;先卸载 sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \d…

【Vue】权限控制

权限管理 分类&#xff1a; 页面权限功能(按钮)权限接口权限 vue3-element-admin 的实现方案 一般我们在业务中将 路由可以分为两种&#xff0c;constantRoutes 和 asyncRoutes。 constantRoutes&#xff1a; 代表那些不需要动态判断权限的路由&#xff0c;如登录页、404(或…

机器学习算法——常规算法,在同的业务场景也需要使用不同的算法(二)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

新兴材料中载流子迁移率的霍尔效应测量

这篇文章是发表在《自然电子学》&#xff08;Nature Electronics&#xff09;2024年7月刊上的一篇评论文章&#xff0c;标题为“Reporting Hall effect measurements of charge carrier mobility in emerging materials”&#xff0c;作者是Vladimir Bruevich和Vitaly Podzorov…

数据仓库及数仓架构概述

往期推荐 大数据HBase图文简介-CSDN博客 数仓常见名词解析和名词之间的关系-CSDN博客 目录 0. 前言 0.1 浅谈维度建模 0.2 数据分析模型 1. 何为数据仓库 1.1 为什么不直接用业务平台的数据而要建设数仓&#xff1f; 1.2 数据仓库特征 1.3 数据仓库和数据库区别 1.4 以…

LLM 各种技巧| Prompt Engineering 大总结|指南

LLM 各种技巧| Prompt Engineering 大总结|指南 截止至今&#xff0c;关于LLM 的优化与技巧层出不穷&#xff0c;几乎每个月都有新的技术和方法论被提出&#xff0c;因此本篇主要是要介绍在各种不同情境下&#xff0c;LLM 的各种Prompt Engineering 技巧&#xff0c;每篇都有附…

数据结构经典测试题5

1. int main() { char arr[2][4]; strcpy (arr[0],"you"); strcpy (arr[1],"me"); arr[0][3]&; printf("%s \n",arr); return 0; }上述代码输出结果是什么呢&#xff1f; A: you&me B: you C: me D: err 答案为A 因为arr是一个2行4列…

使用AWS CDK构建生产级VPC基础设施指南

简介 虽然有很多关于AWS的信息&#xff0c;但实际如何将这些服务投入生产使用&#xff0c;还是需要自己思考。本文将介绍我们是如何思考并实施这些工作的。 目前有很多AWS环境构建的方法&#xff0c;但在这里我们将使用AWS CDK进行说明。 ※ 本文不会涉及CDK的基本操作方法或…

Java每日一练,技术成长不间断

目录 题目1.下列关于继承的哪项叙述是正确的&#xff1f;2.Java的跨平台特性是指它的源代码可以在多个平台运行。&#xff08;&#xff09;3.以下 _____ 不是 Object 类的方法4.以下代码&#xff1a;5.下面哪个流类不属于面向字符的流&#xff08;&#xff09;总结 题目 选自牛…

AI系统测试方法|变异测试的流程及优化技术

变异测试是AI系统测试中较为常见的一种测试方法。通过引入人工制造的缺陷来评估系统的健壮性。在AI系统测试实践中&#xff0c;变异测试解决了对测试套件进行有效性和充分性评估的难题。本文将重点探讨变异测试在AI系统测试中的执行流程&#xff0c;呈现一个完整的测试框架&…