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

news2024/11/26 21:48:11

题目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.coloradmin.cn/o/1296328.html

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

相关文章

基于深度学习yolov5实现安全帽人体识别工地安全识别系统-反光衣识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 实现安全帽人体识别工地安全识别系统需要使用深度学习技术&#xff0c;特别是YOLOv5算法。下面是对基于YOLOv5实现安…

[mac系统]利用换行符查找替换^p 报错 --caption_column‘ calue ‘test‘ needs to be one of: image

报错内容 代码内容 args.image_column "image" args.caption_column "text" 问题原因&#xff1a; 训练过程需要blip文件是metadata.json格式 ​ 测试过程需要的文件是txt格式 blip.txt​ ​ 解决办法 1 利用word查找替换 用{"file_name": &…

银行卡二要素API的应用案例:从在线购物到金融投资

引言 随着互联网技术的不断发展&#xff0c;人们的金融需求也在不断增加。随之而来的是各种新型金融服务的涌现&#xff0c;让用户的金融体验更加便利快捷。其中&#xff0c;银行卡二要素API的应用&#xff0c;则为用户的金融体验和安全性提供了极大的保障。 银行卡二要素API…

【C++数据结构 | 图速通】10分钟掌握邻接矩阵 邻接表 | 快速掌握图论基础 | 快速上手抽象数据类型图

图 by.Qin3Yu 请注意&#xff1a;严格来说&#xff0c;图不是一种数据结构&#xff0c;而是一种抽象数据类型。但为了保证知识点之间的相关性&#xff0c;也将其列入数据结构专栏。 本文需要读者掌握顺序表和单链表的操作基础&#xff0c;若需学习&#xff0c;可参阅我的往期文…

数据清洗、特征工程和数据可视化、数据挖掘与建模的应用场景

1.5 数据清洗、特征工程和数据可视化、挖掘建模的应用场景 视频为《Python数据科学应用从入门到精通》张甜 杨维忠 清华大学出版社一书的随书赠送视频讲解1.5节内容。本书已正式出版上市&#xff0c;当当、京东、淘宝等平台热销中&#xff0c;搜索书名即可。内容涵盖数据科学应…

PostgreSQL从小白到高手教程 - 第38讲:数据库备份

PostgreSQL从小白到专家&#xff0c;是从入门逐渐能力提升的一个系列教程&#xff0c;内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容&#xff0c;希望对热爱PG、学习PG的同学们有帮助&#xff0c;欢迎持续关注CUUG PG技术大讲堂。 第38讲&#…

企业博客SEO:优化SOP,助您提升搜索引擎可见性

企业博客是互联网时代企业与用户沟通的重要渠道之一&#xff0c;引流成本也比较低。然而&#xff0c;依然有企业会处在3种状态&#xff1a; 1. 有博客&#xff0c;但内容更新不积极或搁置 2. 有博客&#xff0c;但内容散乱 3. 根本就没有博客 如果是这几种状态&#xff0c;…

uniapp,点击选中并改变颜色,第二次点击取消选中状态

一、效果图 二、代码实现 字符串的indexOf和数组的indexOf用法一致&#xff01; arr.indexOf(item) 该方法返回某个元素在数组中的位置。若没检索到&#xff0c;则返回 -1。 关键代码&#xff1a;(通过:class绑定) :class"selectList.indexOf(sub.type) ! -1 ? right_ite…

ambari hive on Tez引擎一直卡住

hive on tez使用./bin/hive启动后一直卡住&#xff0c;无法进入命令行 使用TEZ作为Hive默认执行引擎时&#xff0c;需要在调用Hive CLI的时候启动YARN应用&#xff0c;预分配资源&#xff0c;这需要花一些时间&#xff0c;而使用MapReduce作为执行引擎时是在执行语句的时候才会…

Selenium+Unittest+HTMLTestRunner框架更改为Selenium+Pytest+Allure(二)

1 代码框架 整体项目结构如图&#xff1a; Common&#xff1a;公共库 Logs&#xff1a; 日志目录 Page&#xff1a; 页面元素 Report&#xff1a;测试报告 TestCase&#xff1a;测试用例 TestData&#xff1a; 测试数据 2 单模块运行 直接上代码&#xff1a; # -*- coding…

mysql 主从搭建、django实现读写分离、django中多redis缓存、django中使用连接池、pycharm远程linux开发

1 mysql 主从搭建 2 django实现读写分离 3 django中多redis缓存 4 django中使用连接池 5 pycharm远程linux开发 1 mysql 主从搭建 # 之前做过redis的主从&#xff0c;很简单# mysql 稍微复杂一些&#xff0c; 搭建mysql主从的目的是&#xff1f;-读写分离-单个实例并发量低&…

VUE+webrtc-streamer 实现实时视频播放(监控设备-rtsp)

效果 下图则启动成功&#xff0c;此时在浏览器访问127.0.0.1:8000可以看到本机监控画面 1、下载webrtc-streamer 地址&#xff1a;https://github.com/mpromonet/webrtc-streamer/releases 2、解压下载包 3、双击webrtc-streamer.exe启动服务 4、将下载包html文件夹下webrt…

Vue3使用Tailwind CSS

安装 Tailwind 以及其它依赖项 npm install -D tailwindcsslatest postcsslatest autoprefixerlatest生成配置文件&#xff1a; npx tailwindcss init -p.修改配置文件 tailwind.config.js 2.6版本 &#xff1a; module.exports {purge: [./index.html, ./src/**/*.{vue,j…

(三潮来袭)探寻2023年科技变革潮流与2024年前瞻展望

2023年对于IT行业来说是一个动荡而又充满变革的一年。随着世界逐渐走出前几年的挑战&#xff0c;企业逐渐复苏&#xff0c;但这个行业仍然在经历着激烈的变革。在这个时候&#xff0c;我们看到了一些引人注目的技术变化和未来的趋势。 一、2023年回顾 关键词&#xff1a;Chat…

eve-ng镜像模拟设备-信息安全管理与评估-2023国赛

eve-ng镜像模拟设备-信息安全管理与评估-2023国赛 author&#xff1a;leadlife data&#xff1a;2023/12/4 mains&#xff1a;EVE-ng 模拟器 - 信息安全管理与评估模拟环境部署 references&#xff1a; EVE-ng 官网&#xff1a;https://www.eve-ng.net/EVE-ng 中文网&#xff1…

Elasticsearch:什么是检索增强生成 (RAG)?

检索增强生成 (RAG) 定义 检索增强生成 (RAG) 是一种利用来自私有或专有数据源的信息来补充文本生成的技术。 它将旨在搜索大型数据集或知识库的检索模型与大型语言模型 (LLM) 等生成模型相结合&#xff0c;后者获取该信息并生成可读的文本响应。 检索增强生成可以通过添加来…

Navicat 技术指引 | 适用于 GaussDB 分布式的调试器

Navicat Premium&#xff08;16.3.3 Windows 版或以上&#xff09;正式支持 GaussDB 分布式数据库。GaussDB 分布式模式更适合对系统可用性和数据处理能力要求较高的场景。Navicat 工具不仅提供可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结…

项目优化(异步化)

项目优化&#xff08;异步化&#xff09; 1. 认识异步化 1.1 同步与异步 同步&#xff1a;一件事情做完&#xff0c;再做另外一件事情&#xff0c;不能同时进行其他的任务。异步&#xff1a;不用等一件事故完&#xff0c;就可以做另外一件事情。等第一件事完成时&#xff0c…

拦截 open调用 (进程白名单,文件白名单)

拦截 open 文章目录 拦截 open第一个需求文件结构进程白名单文件白名单 测试代码第一个版本版本二代码演示 增加一个日志记录代码解释 gcc -shared -fPIC -o libintercept.so intercept.c -ldlLD_PRELOAD./libintercept.so ./processA在Linux中&#xff0c;我们可以使用LD_PREL…

Navicat 技术指引 | 适用于 GaussDB 分布式的用户/权限功能

Navicat Premium&#xff08;16.3.3 Windows 版或以上&#xff09;正式支持 GaussDB 分布式数据库。GaussDB 分布式模式更适合对系统可用性和数据处理能力要求较高的场景。Navicat 工具不仅提供可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结…