LeetCode(42.接雨水)多解法详解
接雨水解法详解:
题目:
基本思路:从图上可以看出要想接住雨水,必须是凹字形的,也就是当前位置的左右两边必须存在高度大于它的地方,所以我们要想知道当前位置最多能存储多少水,只需找到左边最高处max_left
和右边最高处max_right
,取他们两个较小的那边计算即可(短板效应)。
其实接下来的解法要解决的问题就是如何找到max_right
和max_left
。
不过我们首先来看一个无法AC的解法:
解法一:按行
按行顾名思义就是一行一行地进行计算,首先我们计算第一行,设置一个变量temp临时存储当前接的水和开始标志isStart,当碰到第一个高度大于等于行数的位置时,temp置0,开始计算,接下来如果碰到小于行数的位置时,temp+1
,碰到大于行数的位置时,temp置0,继续往后遍历,如此遍历完一行继续遍历下一行。
class Solution {
public int trap(int[] height) {
int sum = 0;
int max = getMax(height);//找到最大的高度,以便遍历。
for (int i = 1; i <= max; i++) {
boolean isStart = false; //标记是否开始更新 temp
int temp_sum = 0;
for (int j = 0; j < height.length; j++) {
if (isStart && height[j] < i) {
temp_sum++;
}
if (height[j] >= i) {
sum = sum + temp_sum;
temp_sum = 0;
isStart = true;
}
}
}
return sum;
}
private int getMax(int[] height) {
int max = 0;
for (int i = 0; i < height.length; i++) {
if (height[i] > max) {
max = height[i];
}
}
return max;
}
}
不过此解法会在最后两个测试用例处,TLE掉,我们只需了解一下这种思想,medium难度的题还是可以过的。
解法二:按列
这个解法就是我们刚开始所说思路的最朴素的解法了,要求i
位置接水量,只需找到i
左边最高位置的高度max_left
和右边最高位置的高度max_right
,然后取较小的那个min_height=min(max_right,max_left)
,最后计算i
位置接水量:min_height-height[i]
,当然如果min_height<height[i]
,就不用计算啦。
class Solution {
public int trap(int[] height) {
int ans=0;
for(int i=1;i<height.length-1;i++){//第一个和最后一个位置肯定存不了水
int max_left=0;
for(int j=i-1;j>=0;j--){//找到当前位置左边最高处
max_left=Math.max(max_left,height[j]);
}
int max_right=0;
for(int j=i+1;j<height.length;j++)//找到当前位置右边最高处
max_right=Math.max(max_right,height[j]);
int min_high=Math.min(max_right,max_left);
if(min_high>height[i]){
ans+=min_high-height[i];
}
}
return ans;
}
}
解法三:动态规划
我们知道解法二每到一个位置都会遍历左右两边来寻找它的左右最高处,这样就会导致O(n^2)的时间复杂度,我们能不能事先就找好每个位置对应的max_left
和max_right
,显而易见是可以的,所以我们需要声明两个数组max_left
和max_right
来存储每个位置对应的左右最高点,因为我们只在它左右两边寻找,我们可以写出当前位置i
的max_left[i]=max(max_left[i-1],height[i-1])
,右边同理,接下来就是和解法二一样了。
class Solution {
public int trap(int[] height) {
int[] max_left=new int[height.length];
int[] max_right=new int[height.length];
int ans=0;
for(int i=1;i<height.length-1;i++){
max_left[i]=Math.max(max_left[i-1],height[i-1]);
}
for(int i=height.length-2;i>=1;i--){
max_right[i]=Math.max(max_right[i+1],height[i+1]);
}
for(int i=1;i<height.length-1;i++){
int min_height=Math.min(max_right[i],max_left[i]);
if(min_height>height[i]){
ans+=min_height-height[i];
}
}
return ans;
}
}
解法四:双指针
从动态规划解法可以看出,只要max_left[i]<max_right[i]
,积水的高度由max_left[i]
决定,也就是积水的高度是由较低的那边决定,所以此时我们应该继续由较低->较高那个方向遍历,直到此时较低的这边发现比另一边更高的位置,再转换方向,这样我们就可以一次遍历且只需两个指针便可完成计算。
- 初始化 left 指针为 0 并且 right 指针为 size-1
While left<right, do:
If height[left] < height[right]
If height[left]≥left_max, 更新 left_max
Else left_max−height[left] 到ans
left = left + 1.
Else
If height[right]≥right_max, 更新 right_max
Else 累加 right_max−height[right] 到 ans
right = right - 1.
int trap(vector<int>& height)
{
int left = 0, right = height.size() - 1;
int ans = 0;
int left_max = 0, right_max = 0;
while (left < right) {
if (height[left] < height[right]) {
height[left] >= left_max ? (left_max = height[left]) : ans += (left_max - height[left]);
++left;
}
else {
height[right] >= right_max ? (right_max = height[right]) : ans += (right_max - height[right]);
--right;
}
}
return ans;
}
解法五:单调栈
思路:单调栈可以保证栈底到栈顶是单调递减的,也就是说栈顶元素可以由它前一个元素所界定,我们遍历数组时可以维护一个单调栈来存储索引,当当前元素小于栈顶元素时,入栈,当当前元素大于等于栈顶元素时,此时栈顶元素
的接水量就取决于其前一个元素
和当前元素
较小的那个,栈顶元素出栈。
算法:
- 使用栈来存储条形块的索引下标。
- 遍历数组:
- 当栈非空且 height[current]>height[st.top()]
- 意味着栈中元素可以被弹出。弹出栈顶元素
- 计算当前元素和栈顶元素的距离,准备进行填充操作
distance=current−st.top()−1
- 找出界定高度
- bounded_height=min(height[current],height[st.top()])−height[top]
- 往答案中累加积水量ans+=distance×bounded_height
- 将当前索引下标入栈
- 将 current 移动到下个位置
int trap(vector<int>& height)
{
int ans = 0, current = 0;
stack<int> st;
while (current < height.size()) {
while (!st.empty() && height[current] > height[st.top()]) {
int top = st.top();
st.pop();
if (st.empty())
break;
int distance = current - st.top() - 1;
int bounded_height = min(height[current], height[st.top()]) - height[top];
ans += distance * bounded_height;
}
st.push(current++);
}
return ans;
}
总结:
这种多解法的题目可以尽量去了解它的每一种解法,这对扩展自己的解题思维有很大的帮助,其实这个题目这么多解法大部分都是在解决怎么高效找出左右两边最高点,时间复杂度由O(n^2)提升到O(n),空间复杂度也由O(n)->O(1),所以有时候很多方法都是由暴力逐渐改善的,本人表达可能词不达意,还请谅解,如有任何问题,请留言指出,不甚感激。
参考出处
LeetCode(42.接雨水)多解法详解的更多相关文章
- leetcode#42 Trapping rain water的五种解法详解
leetcode#42 Trapping rain water 这道题十分有意思,可以用很多方法做出来,每种方法的思想都值得让人细细体会. 42. Trapping Rain WaterGiven n ...
- Java实现 LeetCode 42 接雨水
42. 接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这 ...
- [LeetCode]42. 接雨水(双指针,DP)
题目 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下, ...
- leetcode 42. 接雨水 JAVA
题目: 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下 ...
- HDU 4622 Reincarnation Hash解法详解
今天想学字符串hash是怎么弄的.就看到了这题模板题 http://acm.hdu.edu.cn/showproblem.php?pid=4622 刚开始当然不懂啦,然后就上网搜解法.很多都是什么后缀 ...
- Leetcode 42.接雨水
接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下 ...
- Leetcode 42 接雨水 双指针
地址 https://leetcode-cn.com/problems/trapping-rain-water/ 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能 ...
- LeetCode 42. 接雨水(Trapping Rain Water)
题目描述 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况 ...
- C++旋转数组(三种解法详解)
题目描述 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数. 附加要求 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题. 你可以使用空间复杂度为 O(1) 的 原地 ...
随机推荐
- svg和css3创建环形渐变进度条
在负责的项目中,有一个环形渐变读取进度的效果的需求,于是在网上查阅相关资料整理一下.代码如下: <!DOCTYPE html> <html lang="en"&g ...
- jspdf + html2canvas 实现html转pdf (提高分辨率版本)
刚解决了html中某div块生成pdf的问题,热乎乎的,赶紧记录下 引入的js传送门: https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.0.272/jsp ...
- html+css+js+Hbuilder开发一款安卓APP,根本不用学Android开发!
我们知道,要做一款安卓APP,咱们得先学安卓开发语言,例如java,前端后端.那么没有这些开发语言基础,咱们怎么做呢?其实现在有比较好的开发方案就是做webAPP,咱们可以用web前端知识构建安卓客户 ...
- 通过filebeat、logstash、rsyslog采集nginx日志的几种方式
由于nginx功能强大,性能突出,越来越多的web应用采用nginx作为http和反向代理的web服务器.而nginx的访问日志不管是做用户行为分析还是安全分析都是非常重要的数据源之一.如何有效便捷的 ...
- Feign 在 SpringCloud 中的使用 四
此处就单纯写一个消费者服务,通过Feign来调用生产者中的接口,生产者中的接口可以自己随便定义一个,前面博客中也有代码 1.导入springcloud Feign的jar包 <parent> ...
- 《前端之路》 - 初试 TypeScript(一)基础数据类型
一.先讲讲 TypeScript 什么是 typeScript ? typeScript 是 Javascript 的超集 我们用一张图来简单介绍下 ts 和 js 清清楚楚明明白白的关系- 为什么会 ...
- 029.核心组件-Controller Manager
一 Controller Manager原理 1.1 Controller Manager概述 一般来说,智能系统和自动系统通常会通过一个"控制系统"来不断修正系统的工作状态.在K ...
- node 微信退款
基于node 的微信退款 申请微信退款:微信退款, 1.在前端页面访问 /refund var request = require('request'); var WxPayRefund = req ...
- Andorid 添加MenuPopup
- RTSP协议进行视频取流的方法、注意点及python实现
在视频应用中,我们一般都需要基于摄像头或录像机的视频流进行二次开发,那么就涉及到如何将视频流取出来. 在摄像机安装好之后,一般是通过局域网与本地的服务器进行连接,要取录像机的视频流就要在局域网范围内进 ...