leetcode 42. 接雨水 思考分析(暴力、动态规划、双指针、单调栈)

目录

    • 题目
    • 思路
    • 暴力法
    • 动态规划
    • 双指针法
    • 单调栈

题目

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
在这里插入图片描述

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

提示:
n == height.length
0 <= n <= 3 * 10^4
0 <= height[i] <= 10^5

思路

对于数组中的每个元素,最高水位为:两边最大高度的较小值 - 当前高度的值
用伪代码描述:
water[i] = min(left,right) - height[i];
(有一说一,没想到是对每个元素进行计算的,以为是找到几个较高点,然后划分区间分别计算。)
这样问题就可以化简为寻找每个元素右边最大的值和左边最大的值了。
没想到这个,我感觉很难往下做下去。

暴力法

使用暴力法,对于遍历数组的时候,对应的元素找左右最大值,顺着上面的思路来一遍:
显然时间复杂度是O(n^2)的

class Solution {
public:int trap(vector<int>& height){int ans = 0;int n = height.size();for(int i = 0; i < n; i++){//找左右最大值int left_max = 0, right_max = 0;for(int j = i; j >= 0; j--)left_max = max(left_max,height[j]);for(int j = i; j < n; j++)right_max = max(right_max,height[j]);//累积此位置的雨水ans += min(left_max,right_max) - height[i];}return ans;}
};

动态规划

之前,对于每个元素的左右最大值我们是在遍历该元素的时候找的。可以预先找到,存储下来,计算雨水的时候直接查询即可。
需要预先构造出两个数组,用来存放。
填充极大值数组的时候要记住:
从左向右遍历,每次更新左极大值,当前元素的左极大值只和当前元素和左边一个元素的左极大值有关。
即:left_max[i] = max(height[i],left_max[i - 1]);
从右向左遍历,每次更新右极大值,当前元素的右极大值只和当前元素和右边一个元素的右极大值有关
即:right_max[i] = max(height[i],right_max[i + 1]);
这个方法有两个细节:
1、考虑输入数组为空
2、初始化左右极大值数组的第一个元素以及遍历的起点

显然,时间复杂度是O(n)的。是遍历了3次原数组。

class Solution {
public:int trap(vector<int>& height){if(height.empty()) return 0;int ans = 0;int n = height.size();vector<int> left_max(n);vector<int> right_max(n);//初始化//第一个元素的左边没有元素,所以左极大值就是本身left_max[0] = height[0];//最后一个元素的右边没有元素,所以右极大值就是本身right_max[n - 1] = height[n - 1];//从左向右遍历,每次更新左极大值,当前元素的左极大值只和当前元素和左边一个元素的左极大值有关for(int i = 1; i < n; i++)left_max[i] = max(height[i],left_max[i - 1]);//从右向左遍历,每次更新右极大值,当前元素的右极大值只和当前元素和右边一个元素的右极大值有关for(int i = n - 2; i >= 0; i--)right_max[i] = max(height[i],right_max[i + 1]);for(int i = 0; i < n; i++){ans += min(left_max[i],right_max[i]) -height[i];}return ans;}
};

双指针法

动态规划是遍历了两次,使用双指针可以实现只遍历一次就实现填充左右极大值数组。
感觉有个解释解释的非常好:
https://leetcode-cn.com/problems/trapping-rain-water/solution/jie-yu-shui-by-leetcode/327718

1、明确变量意义

left_max:左边的最大值,它是从左往右遍历找到的
right_max:右边的最大值,它是从右往左遍历找到的
left:从左往右处理的当前下标
right:从右往左处理的当前下标

2、明确已知定理:

定理一:在某个位置i处,它能存的水,取决于它左右两边的最大值中较小的一个。
定理二:当我们从左往右处理到left下标时,左边的最大值left_max对它而言是可信的,但right_max对它而言是不可信的。(见下图,由于中间状况未知,对于left下标而言,right_max未必就是它右边最大的值)
定理三:当我们从右往左处理到right下标时,右边的最大值right_max对它而言是可信的,但left_max对它而言是不可信的。

3、得到具体细节

对于位置left而言,它左边最大值一定是left_max,右边最大值“大于等于”right_max,这时候,如果left_max<right_max成立,那么它就知道自己能存多少水了。无论右边将来会不会出现更大的right_max,都不影响这个结果。 所以当left_max<right_max时,我们就希望去处理left下标,反之,我们希望去处理right下标。

4、知道了这些,就可以写代码了:

class Solution {
public:int trap(vector<int>& height){if(height.empty()) return 0;int ans = 0;int n = height.size();int left = 0,right = n - 1;int left_max = height[left], right_max = height[right];while(left <= right){if(left_max < right_max){//还要判断left_max与当前的值谁大,如果当前值大,就不需要累积了。if(left_max < height[left]) left_max = height[left];else    ans += (left_max - height[left]);left++;}else{//还要判断left_max与当前的值谁大,如果当前值大,就不需要累积了。if(right_max < height[right]) right_max = height[right];else    ans += (right_max - height[right]);right--;}}return ans;}
};

单调栈

尽管之前写过接近8题的单调栈的题目,这一题我使用单调栈的思路感觉并不是很清晰。而且粗略看了官方题解也没理解具体细节。
下面两个单调栈的讲法都比较好:
我下面使用的图也是从这两个地方嫖的。
https://leetcode-cn.com/problems/trapping-rain-water/solution/42-jie-yu-shui-shuang-zhi-zhen-dong-tai-gui-hua-da/
https://leetcode-cn.com/problems/trapping-rain-water/solution/dan-diao-zhan-jie-jue-jie-yu-shui-wen-ti-by-sweeti/
单调栈计算雨水的方式是按照行计算的:
在这里插入图片描述
在这里插入图片描述
还有两个个需要注意的地方:
1、我们构建的是单调递减栈,一旦发现添加的柱子高度大于栈头元素了,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。
如下图所示:
在这里插入图片描述
2、遇到相同高度的柱子怎么办。
遇到相同的元素,更新栈内下标,就是将栈里元素(旧下标)弹出,将新元素(新下标)加入栈中。
因为我们要求宽度的时候 如果遇到相同高度的柱子,需要使用最右边的柱子来计算宽度。
在这里插入图片描述
接下来就可以套用模板写代码了
这里借鉴了代码随想录的冗余代码,能够较好地理解for循环中三个情况具体处理。

class Solution {
public:int trap(vector<int>& height){if(height.empty()) return 0;int ans = 0;int n =height.size();vector<int> st;for(int i = 0; i < n; i++){//如果栈为空,或者栈顶元素大于height[i]入栈if( st.empty() || height[i] < height[st.back()]){st.emplace_back(i);}//如果栈不为空且栈顶元素==height[i],更新栈顶元素,取更右边的else if(!st.empty() && height[i] == height[st.back()]){st.pop_back();st.emplace_back(i);}//如果栈不为空,且栈顶元素小于height[i]else{while(!st.empty() && height[i] > height[st.back()]){int mid = st.back();st.pop_back();if(!st.empty()){int H = min(height[st.back()],height[i]) - height[mid];int W = i - st.back() -1;       //i与st.back()之间的宽度ans += H * W;}}st.emplace_back(i);}}return ans;}
};

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

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

相关文章

chdir函数_PHP chdir()函数与示例

chdir函数PHP chdir()函数 (PHP chdir() function) The full form of chdir is "Change Directory", the function chdir() is used to change the current working directory. chdir的完整形式是“更改目录” &#xff0c; 功能chdir()用于更改当前工作目录。 Synt…

十二、Sequential

一、Sequential介绍 torch.nn.Sequential(*args) 由官网给的Example可以大概了解到Sequential是将多层网络进行便捷整合&#xff0c;方便可视化以及简化网络复杂性 二、复现网络模型训练CIFAR-10数据集 这里面有个Hidden units隐藏单元其实就是连个线性层 把隐藏层全部展开整…

1064-快速排序

描述 给定输入排序元素数目n和相应的n个元素&#xff0c;写出程序&#xff0c;利用内排序算法中快速排序算法进行排序&#xff0c;并输出排序最后结果的相应序列。 输入 共两行&#xff0c;第一行给出排序元素数目n&#xff0c;第二行给出n个元素&#xff0c;1≤n≤100000&…

社交问答:取代BBS的Web2.0革命

编者按&#xff1a;本文由乐维UP创始人俞越撰写&#xff0c;你也可以点击这里关注俞越的新浪微博。 BBS在中国的兴起是在95年&#xff0c;之后以惊人的速度发展起来。从2011年开始&#xff0c;国内的问答社区也如当年的BBS一样&#xff0c;大量涌现快速成长&#xff0c;大体分为…

单调栈 leetcode整理(三)

目录42. 接雨水思路分析901. 股票价格跨度思路581. 最短无序连续子数组思路一&#xff1a;排序双指针思路二&#xff1a;单调栈思路三&#xff1a;双指针(最省时)42. 接雨水 42. 接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&…

python 抠图 锯齿_Python | 绘图中的抗锯齿

python 抠图 锯齿Antialiasing is another important feature of Matplotlib and in this article, we will review how to use this functionality. pyplot.antialiased() is an inbuilt function in matplotlib.pyplot which performs our required operation. 抗锯齿是Matpl…

apk 反编译

引用&#xff1a;http://code.google.com/p/dex2jar/issues/detail?id20 最新版:dex2jar http://code.google.com/p/dex2jar/downloads/list 错误&#xff1a;http://code.google.com/p/dex2jar/issues/detail?id20 这段时间在学Android应用开发&#xff0c;在想既然是用Jav…

OpenDiscussion_DataDrivenDesign

本文源于公司内部技术交流&#xff0c;如有不当之处&#xff0c;还请指正。 Content&#xff1a; 1. What is Data-driven design?2. WPF revolution.3. More about ObservableCollection.4. Question.1. What is Data-driven design?Data-driven design: is a design of usi…

十三、Loss Functions

一、Loss Functions损失函数 损失函数的作用&#xff1a; 1&#xff0c;损失函数就是实际输出值和目标值之间的差 2&#xff0c;由这个差便可以通过反向传播对之后的数据进行更新 Loss Functions官网给的API 里面由很多种损失函数&#xff0c;不同的损失函数有其不同的用途及表…

leetcode 滑动窗口小结 (一)

目录小结以及代码框架76. 最小覆盖子串滑动窗口代码以及注释567. 字符串的排列滑动窗口438. 找到字符串中所有字母异位词3. 无重复字符的最长子串化简框架reference小结以及代码框架 滑动窗口技巧属于双指针技巧。 该算法的思路为维护一个窗口&#xff0c;不断滑动&#xff0c…

linux命令行界面_Linux中的命令行界面

linux命令行界面If you are like most people, you are probably most familiar with using a Graphical User Interface (GUI) to control your computer. Introduced to the masses by Apple on the Macintosh computer and popularized by Microsoft, a GUI provides an eas…

一道小小面试题的细节分析

一道小小面试题的细节分析 今天突然想到以前遇到的一个问题&#xff0c;题目如下&#xff08;可能绝大多数人都遇到过&#xff09;&#xff1a; 1 class A2 {3 public A()4 {5 PrintFields();6 }7 public virtual void Pr…

十四、OPTIM

一、torch.optim torch.optim.Optimizer(params, defaults)优化器官网说明 由官网给的使用说明打开看出来优化器实验步骤&#xff1a; ①构造选择优化器 例如采用随机梯度下降优化器SGD torch.optim.SGD(beyond.parameters(),lr0.01)&#xff0c;放入beyond模型的参数param…

Windows下运行jekyll,编码已不再是问题

很久没更新jekyll了&#xff0c;所以好奇着去官网看了下更新记录&#xff0c;发现如下更新条目&#xff08;版本1.3.0/2013-11-04发布&#xff09;&#xff1a; Add encoding configuration option (#1449)之前在windows下安装jekyll运行编写的代码时&#xff0c;如果有中文&am…

leetcode 滑动窗口小结 (二)

目录424. 替换后的最长重复字符思考分析1优化1004. 最大连续1的个数 III友情提醒方法1&#xff0c;基于当前最大频数方法2&#xff0c;基于历史最大频数424. 替换后的最长重复字符 https://leetcode-cn.com/problems/longest-repeating-character-replacement/ 给你一个仅由大…

软件故障_一些主要的软件故障

软件故障The need for software engineering was realized by the software industry after some of its major failures. Some of these failures are listed below, 在经历了一些重大失败之后&#xff0c;软件行业意识到了对软件工程的需求 。 下面列出了其中一些故障&#x…

十五、修改VGG16网络来适应自己的需求

一、VGG-16 VGG-16神经网络是所训练的数据集为ImageNet ImageNet数据集中验证集和测试集一万五千张&#xff0c;有一千个类别 二、加载VGG-16神经网络模型 VGG16模型使用说明 torchvision.models.vgg16(pretrainedFalse) 其中参数pretrained表示是否下载已经通过ImageNet数…

leetcode 滑动窗口小结 (三)

目录978. 最长湍流子数组题目思路分析以及代码1052. 爱生气的书店老板题目思考分析与初步代码优化思路以及优化代码1208. 尽可能使字符串相等题目思考分析以及代码978. 最长湍流子数组 https://leetcode-cn.com/problems/longest-turbulent-subarray/ 题目 当 A 的子数组 A[…

JAVA多线程学习3--线程一些方法

一、通过sleep方法睡眠 在指定的毫秒数内让当前正在执行的线程休眠&#xff08;暂停执行&#xff09;。该线程不丢失任何监视器的所属权。 二、线程优先级 线程具有优先级&#xff0c;范围为1-10。 MAX_PRIORITY线程可以具有的最高优先级。int类型&#xff0c;值为10. MIN_PRIO…

mcq 队列_MCQ | 量子密码学

mcq 队列1) Which possible Attacks in Quantum Cryptography can take place? 1)量子密码术中可能发生哪些攻击&#xff1f; Possible Attacks in Quantum Cryptography and Birthday Attack 量子密码术和生日攻击的可能攻击 Birthday attack and Boomerang attack 生日袭击…