C++ day59 下一个更大元素Ⅱ 接雨水

题目1:503 下一个更大元素Ⅰ

题目链接:下一个更大元素Ⅱ

对题目的理解

返回循环数组中每个元素的下一个更大元素,

数字x的下一个更大元素是循环等的搜索它的最近的下一个更大的数

数组的中至少有一个元素

本题难点在于循环遍历这里,环形数组

法一:合并拼接两个数组

代码

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        vector<int> nums1(nums.begin(),nums.end());
        nums.insert(nums.end(),nums1.begin(),nums1.end());
        vector<int> result(nums.size(),-1);
        stack<int> st;
        st.push(0);
        for(int i=1;i<nums.size();i++){
            if(nums[i]<nums[st.top()]) st.push(i);
            else if(nums[i]==nums[st.top()]) st.push(i);
            else {
                while(!st.empty() && nums[i]>nums[st.top()]){
                    result[st.top()]=nums[i];
                    st.pop();
                }
                st.push(i);
            }
        }
        result.resize(nums.size()/2);
        //result.resize(nums1.size());//这句代码与上一句的作用相同
        return result;
    }
};

精简代码

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        vector<int> nums1(nums.begin(),nums.end());
        nums.insert(nums.end(),nums1.begin(),nums1.end());
        vector<int> result(nums.size(),-1);
        stack<int> st;
        st.push(0);
        for(int i=1;i<nums.size();i++){
            while(!st.empty() && nums[i]>nums[st.top()]){
                result[st.top()]=nums[i];
                st.pop();
            }
            st.push(i);
        }
        result.resize(nums.size()/2);
        //result.resize(nums1.size());//这句代码与上一句的作用相同
        return result;
    }
};

这种写法确实比较直观,但做了很多无用操作,例如修改了nums数组,而且最后还要把result数组resize回去,resize倒是不费时间,是O(1)的操作,但扩充nums数组相当于多了一个O(n)的操作。

法二:取模的方式进行循环成环问题的求解

不扩充nums,而是在遍历的过程中模拟走了两边nums,这就是利用取模的方式解决这种循环的问题

伪代码

代码

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        vector<int> result(nums.size(),-1);
        stack<int> st;
        st.push(0);//在栈中放入第一个元素的下标
        for(int i=1;i<2*nums.size();i++){
            if(nums[i%nums.size()]<nums[st.top()]) st.push(i%nums.size());
            else if(nums[i%nums.size()]==nums[st.top()]) st.push(i%nums.size());
            else {
                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;
    }
};

精简代码

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        vector<int> result(nums.size(),-1);
        stack<int> st;
        for(int i=0;i<2*nums.size();i++){
            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;
    }
};

代码流程

题目2:42 接雨水

题目链接:接雨水

对题目的理解

是面试高频题,是单调栈应用的题目,掌握双指针(更直接)和单调栈(有难度)的方法

暴力解法(纵向求解,按列计算)

按照列计算,宽度是定值1,把每一列的雨水高度求出来就可

每列雨水的高度取决于该列左侧最高的柱子和右侧最高的柱子的值最小的那个柱子的高度,for循环中求左右两边最高柱子,取最小值

从头遍历一遍所有的列,然后求出每一列雨水的面积,相加之后就是总雨水的面积了,注意第一个柱子和最后一个柱子不接雨水

代码1

class Solution {
public:
    int trap(vector<int>& height) {
        //遍历整个列,求出每列雨水的高度,高度*宽度(1)=面积,再加和就是总面积
        int sum = 0;
        for(int i=0;i<height.size();i++){
            if(i==0 || i==height.size()-1) continue;
            int right_height = 0;
            for(int r=i+1;r<height.size();r++){//当前列雨水右边的高度
                //当前列雨水右边的高度大于当前列雨水的高度
                if(height[r]>height[i]) right_height = max(right_height,height[r]);
            }
            int left_height = 0;
            for(int l=i-1;l>=0;l--){//当前列雨水左边的高度
                //当前列雨水左边的高度大于当前列雨水的高度
                if(height[l]>height[i]) left_height = max(left_height,height[l]);
            }
            int h = min(left_height,right_height)-height[i];//凹槽雨水的高度
            if(h>0) sum += h;//注意只有h大于0的时候,才做加和,
            //可能会出现高度一直递减的情况,右边高度和左边高度一直为0,那么求得的差值就小于0
        }
        return sum;
    }
};

代码2

class Solution {
public:
    int trap(vector<int>& height) {
        //遍历整个列,求出每列雨水的高度,高度*宽度(1)=面积,再加和就是总面积
        int sum = 0;
        for(int i=0;i<height.size();i++){
            if(i==0 || i==height.size()-1) continue;
            int right_height = height[i];
            for(int r=i+1;r<height.size();r++){//当前列雨水右边的高度
                //当前列雨水右边的高度大于当前列雨水的高度
                if(height[r]>right_height) right_height = height[r];
            }
            int left_height = height[i];
            for(int l=i-1;l>=0;l--){//当前列雨水左边的高度
                //当前列雨水左边的高度大于当前列雨水的高度
                if(height[l]>left_height) left_height = height[l];
            }
            int h = min(left_height,right_height)-height[i];//凹槽雨水的高度
            if(h>0) sum += h;//注意只有h大于0的时候,才做加和,
            //可能会出现高度一直递减的情况,右边高度和左边高度一直为0,那么求得的差值就小于0
        }
        return sum;
    }
};

每次遍历列的时候,还要向两边寻找最高的列,时间复杂度为O(n^2),空间复杂度为O(1)。

上述两段代码会超时

双指针

暴力解法中为了得到两边的最高高度,使用双指针遍历,每到一个柱子都向两边遍历一遍,这其实是有重复计算的。我们把每一个位置的左边最高高度记录在一个数组上(maxLeft),右边最高高度记录在一个数组上(maxRight),这样就避免了重复计算

当前位置,左边的最高高度是前一个位置的左边最高高和本高度的最大值

代码

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);
        //每个柱子左边柱子的最大高度
        maxleft[0] = height[0];
        for(int i=1;i<height.size();i++){
            maxleft[i]=max(height[i],maxleft[i-1]);
        }
        //每个柱子右边柱子的最大高度
        maxright[height.size()-1]=height[height.size()-1];
        for(int i=height.size()-2;i>=0;i--){
            maxright[i]=max(height[i],maxright[i+1]);
        }
        //求面积和
        int sum=0;
        for(int i=0;i<height.size();i++){
            int h=min(maxleft[i],maxright[i])-height[i];
            if(h>0) sum += h;
        }
        return sum;
    }
};

上述双指针优化过的代码就不会报超时错误了,可以顺利运行

单调栈(横向求解,按行计算)

接雨水这道题目,需要寻找一个元素,右边最大元素以及左边最大元素,来计算雨水面积,因此使用单调栈。

添加的柱子高度大于栈头元素了,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。

遇到相同的元素,更新栈内下标,就是将栈里元素(旧下标)弹出,将新元素(新下标)加入栈中求宽度的时候 如果遇到相同高度的柱子,需要使用最右边的柱子来计算宽度

长 * 宽 来计算雨水面积的,长就是通过柱子的高度来计算,宽是通过柱子之间的下标来计算

栈里就存放下标就行,想要知道对应的高度,通过height[stack.top()] 就知道弹出的下标对应的高度了。

逻辑主要就是三种情况

  • 情况一:当前遍历的元素(柱子)高度小于栈顶元素的高度 height[i] < height[st.top()] 当前元素入栈
  • 情况二:当前遍历的元素(柱子)高度等于栈顶元素的高度 height[i] == height[st.top()] 栈顶元素弹出,当前元素入栈
  • 情况三:当前遍历的元素(柱子)高度大于栈顶元素的高度 height[i] > height[st.top()]  此时出现凹槽,取栈顶元素,将栈顶元素弹出,这个就是凹槽的底部,也就是中间位置,下标记为mid,对应的高度为height[mid];此时的栈顶元素st.top(),就是凹槽的左边位置,下标为st.top(),对应的高度为height[st.top()],当前遍历的元素i,就是凹槽右边的位置,下标为i,对应的高度为height[i],其实就是栈顶和栈顶的下一个元素以及要入栈的元素,三个元素来接水!雨水高度是 min(凹槽左边高度, 凹槽右边高度) - 凹槽底部高度,雨水的宽度是 凹槽右边的下标 - 凹槽左边的下标 - 1(因为只求中间宽度),当前凹槽雨水的体积就是:高度*宽度

伪代码

代码

class Solution {
public:
    int trap(vector<int>& height) {
        if(height.size()<=2) return 0;//如果数组中至多有两个元素,肯定不能形成凹槽
        int sum = 0;
        stack<int> st;
        st.push(0);
        for(int i=1;i<height.size();i++){
            if(height[i]<height[st.top()]) st.push(i);
            else if(height[i]==height[st.top()]){
                st.pop();
                st.push(i);
            }
            else {
                while(!st.empty() && height[i]>height[st.top()]){
                    int mid = st.top();//记录中间元素
                    st.pop();
                    if(!st.empty()){//前面将top元素弹出,因此判断st是否为空
                        int h = min(height[i],height[st.top()])-height[mid];
                        int w = i-st.top()-1;
                        sum += h*w;
                    }
                }
                st.push(i);
            }
        }
        return sum;
    }
};

精简代码

class Solution {
public:
    int trap(vector<int>& height) {
        if(height.size()<=2) return 0;//如果数组中至多有两个元素,肯定不能形成凹槽
        int sum = 0;
        stack<int> st;
        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()){//前面将top元素弹出,因此判断st是否为空
                    int h = min(height[i],height[st.top()])-height[mid];
                    int w = i-st.top()-1;
                    sum += h*w;
                }
            }
            st.push(i);
        }
        return sum;
    }
};

代码流程

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

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

相关文章

新手搭建知识付费平台必备攻略:如何以低成本实现高转化?

我有才知识付费平台 一、引言 随着知识经济的崛起&#xff0c;越来越多的知识提供者希望搭建自己的知识付费平台。然而&#xff0c;对于新手来说&#xff0c;如何以低成本、高效率地实现这一目标&#xff0c;同时满足自身需求并提高客户转化率&#xff0c;是一大挑战。本文将…

【面试经典150 | 二叉树】从前序与中序遍历序列构造二叉树

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;递归 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题涉及到的数据结构等内容…

Java数字化健康卫生智慧云HIS系统源码

基于云计算技术的B/S架构云HIS集挂号、处方、收费、取药、病历于一体,完全适配各类中小型医院、诊所。 一、云 HIS定义 1、云 HIS 系统是运用云计算、大数据、物联网等新兴信息技术&#xff0c;按照现代医疗卫生管理要求&#xff0c;在一定区域范围内以数字化形式提供医疗卫生…

软文营销全过程分享,助力企业提高宣传效率

数字化时代下&#xff0c;软文营销已经成为许多企业推广品牌的手段&#xff0c;但是在营销过程中大部分企业认为只需要写好文章进行发布就够了&#xff0c;这其实是错误的&#xff0c;只会浪费企业的时间精力。今天媒介盒子就分享软文营销全过程&#xff0c;助力企业提高宣传效…

cpu 300% 爆满 内存占用不高 排查

top查询 cpu最高的PID ps -ef | grep PID 查看具体哪一个jar服务 jstack -l PID > ./jstack.log 下载/打印进程的线程栈信息 可以加信息简单分析 或进一步 查看堆内存使用情况 jmap -heap Java进程id jstack.log 信息示例 Full thread dump Java HotSpot(TM) 64-Bit Se…

SpringMVC 案例

文章目录 前言1. 计算器1.1 准备前端代码1.2 测试前端代码1.3 完成后端代码1.4 验证程序 2. 留言板2.1 前端代码准备2.2 测试前端代码2.3 完成前后端交互代码2.4 完成后端代码2.5 案例测试2.6 完善前后端交互2.7 完善后端代码2.8 完整功能测试 lombok简单的方式添加Lombok工具3…

小视频怎么做成二维码?视频二维码3步生成

在日常工作和生活中经常会看到各种类型的小视频、短视频&#xff0c;比如网页、抖音等等的视频都是可以下载查看的。当我们想要将下载视频分享给多个人看时&#xff0c;生成二维码的方式会更加的方便&#xff0c;那么视频如何生成二维码呢&#xff1f;下面就将快捷生成二维码的…

spring boot 3.2 整合 keycloak

背景 项目中用到 keycloak&#xff0c;因此其他所有管理页面要集成 keycloak 做统一登录认证。 Keycloak 侧配置 容器方式启动 keycloak 服务端 docker run -d --name mykeycloak -p 8080:8080 -e KEYCLOAK_ADMINadmin -e KEYCLOAK_ADMIN_PASSWORDadmin ke…

LeetCode 每日一题 Day 6(DFS+BFS)

1466. 重新规划路线 n 座城市&#xff0c;从 0 到 n-1 编号&#xff0c;其间共有 n-1 条路线。因此&#xff0c;要想在两座不同城市之间旅行只有唯一一条路线可供选择&#xff08;路线网形成一颗树&#xff09;。去年&#xff0c;交通运输部决定重新规划路线&#xff0c;以改变…

【GEE笔记】在线分类流程,标注样本点、分类和精度评价

GEE在线分类流程 介绍 GEE&#xff08;Google Earth Engine&#xff09;是一个强大的地理信息处理平台&#xff0c;可以实现在线的遥感影像分析和处理。本文将介绍如何使用GEE进行在线的分类流程&#xff0c;包括标注样本点、分类和精度评价。本文以2020年5月至8月的哨兵2影像…

优秀案例 | 元宇宙双语财经科技主播“舒望”主持首届粤港澳大湾区元宇宙国际传播论坛

12月6日&#xff0c;由南方财经全媒体集团指导、大湾区元宇宙国际传播实验室(GBA MIC Lab&#xff09;主办、南财国际传播中心和21世纪经济报道共同承办&#xff0c;以“多元共创开放共享”为主题的首届粤港澳大湾区元宇宙国际传播论坛在广州隆重开幕。 “立足湾区&#xff0c;…

【GEE笔记】随机森林特征重要性计算并排序

随机森林是一种基于多个决策树的集成学习方法&#xff0c;可以用于分类和回归问题。在gee中可以使用ee.Classifier.smileRandomForest()函数来创建一个随机森林分类器&#xff0c;并用它来对影像进行分类。 随机森林分类器有一个重要的属性&#xff0c;就是可以计算每个特征&a…

【沁恒蓝牙MESH】CH582串口中断内存溢出导致MCU频繁重启

本文主要记录了【沁恒蓝牙mesh】CH582串口中断内存溢出导致MCU频繁重启 由于开发疏忽&#xff0c;导致的数组内存溢出&#xff0c;是入门嵌入式开发经常忽视的错误&#xff0c;用以记录&#xff0c;共勉&#xff01;&#xff01; 目录 1. 遇到问题描述以及解决1.1 问题一&#…

案例063:基于微信小程序的传染病防控宣传系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder …

都2023年了还在搜索Maven是什么?赶紧来学习(超详细一文搞懂)

文章目录 前言1. 到底什么是 Maven2. 为什么要学Maven3. 创建一个 Maven 项目4. Maven 核心功能4.1 项目构建4.2 依赖管理 4. Maven 仓库4.1 本地仓库4.2 中央仓库4.3 私有服务器, 也称为私服 5. Maven 设置国内源5.1 配置当前项目setting5.2 设置新项目的setting 总结 前言 我…

N4694D 电子校准件(ECal),67 GHz,1.85 mm,2 端口

N4694D 电子校准件 Keysight N4694D 微波电子校准件&#xff08;ECal&#xff09;可以快速、轻松和准确地对是德科技矢量网络分析仪进行校准。 N4694D 是一款精密型 2 端口电子校准件&#xff0c;支持 1.85 mm 连接器和高达 67 GHz 的频率范围。 用户可以在阴头-阴头、阳头-阳头…

低代码开发:激发创新还是程序员的末日?

前言 近年来&#xff0c;低代码开发备受关注&#xff0c;引发了市场上的热议。这一新兴技术被标榜为具备低门槛、高效率和易集成等特性&#xff0c;然而&#xff0c;却引发了一系列的争论。究竟低代码是伪需求还是行业创新的助推器&#xff1f;它是否可能让程序员失业&#xf…

电脑报错msvcr100.dll丢失?竟有5种解决方法,全面解析

在计算机的使用过程中&#xff0c;我们可能会遇到各种问题&#xff0c;其中之一就是msvcr100.dll丢失。这个问题主要出现在基于Microsoft Visual Studio 2010开发的程序上&#xff0c;可能导致程序无法正常运行。本文将详细介绍msvcr100.dll是什么&#xff0c;以及如何解决其丢…

强制使用新版,Win11里隐藏的Win10要没了

系统这玩意和游戏一样&#xff0c;在许多人眼中「上一代」永远是最好的一代。 除了尝鲜测试阶段必然出现许多 Bug &#xff0c;另一个原因大概是好不容易建立的使用习惯又被打破。 Win10 到 11 的换代即是如此&#xff0c;就算不提稳定性&#xff0c;也还有一些让人至今难以适…
最新文章