Trapping Rain Water

原题链接Trapping Rain Water

给定一序列,表示一个直方图每个柱的高度(图中黑色部分),计算这个直方图可以存储的容量(图中浅蓝色部分)

直观的想法可能是直接求容量,比如说计算高度为2和高度为3的两个柱子之间的容量,但是需要减去二者之间的其他柱子所占的面积,比较麻烦。

从图中可以看出,对于某个柱子,判断它是否被某两个柱子构成的区域包含的方法,是判断这个柱子左右两边是否都存在比它高的柱子。比如说从左边开始的那个高度为2的柱子,它右边有一个高度为3的柱子,但是左边没有比它再高的柱子了,所以,它不在任两个柱子构成的区域中。

那么可以这样理解,判断一个柱子是否在某两个柱子构成的区域中的方法,是判断它的正上方是否有浅蓝色区域。
相反,判断一个柱子正上方是否有浅蓝色方块,只需要判断这个柱子的左边和右边是否有高度大于它的柱子。而计算正上方浅蓝色方块个数的方法,是计算这个柱子左边高度最大的柱子和右边高度最大的柱子中高度较小的那个柱子的高度和这个柱子的高度差。

而如果垂直x轴的方向看每个柱子正上方的浅蓝色方块,可以发现整个容量恰好是由每个柱子正上方的浅蓝色方块组成(包括高度为0的柱子)。

综上,只需要求每个柱子正上方浅蓝色区域块的个数(因为面积是1),而浅蓝色区域块的个数是通过左边最高高度和右边最大高度中较小的那个高度和当前柱子的高度作差得到。


直接求解

class Solution {
public:int trap(vector<int>& height) {int ans = 0;int n = height.size();for(int i = 1; i < n - 1; ++i){int max_left = 0;int max_right = 0;/* 找当前柱子左边的最大高度,算上当前柱子的高度 *//* 即如果左边没有大于当前柱子的高度,那么最大高度就是当前柱子的高度 */for(int j = i; j >= 0; --j)max_left = std::max(max_left, height[j]);/* 找右边的最大高度,同理 */for(int j = i; j < n; ++j)max_right = std::max(max_right, height[j]);/* 两个高度中较小的高度和当前柱子的高度差 *//* 如果max_left == height[i],那么差为0,上方没有浅蓝色块 */ans += std::min(max_left, max_right) - height[i];}return ans;}
};

动态规划
上面的方式重复计算了很多max_left和max_right,可以用动态规划的思想减少重复计算

class Solution {
public:int trap(vector<int>& height) {if(height.empty())return 0;int n = height.size();/* 对于某个位置i,计算i左边的最大高度 */vector<int> left_dp(n, 0);left_dp[0] = height[0];for(int i = 1; i < n; ++i)left_dp[i] = std::max(left_dp[i - 1], height[i]);/* 对于某个位置i,计算i右边的最大高度 */vector<int> right_dp(n, 0);right_dp[n - 1] = height[n - 1];for(int i = n - 2; i >= 0; --i)right_dp[i] = std::max(right_dp[i + 1], height[i]);int ans = 0;for(int i = 1; i < n - 1; ++i)ans += std::min(left_dp[i], right_dp[i]) - height[i];return ans;}
};

使用两个指针
动态规划的时间复杂度为O(n),空间复杂度也是O(n)。如果要将空间复杂度降到O(1),就需要实时更新max_left和max_right。
可以通过从两边同时向中间逼近的方法,但需要保证

  • 从左边开始遍历到某个柱子时,右边一定有某个柱子的高度大于max_left。此时二者的较小值为max_left
  • 从右边开始遍历到某个柱子时,左边一定有某个柱子的高度大于max_right。此时二者的较小值为max_right

要保证右边一定有某个柱子高度大于max_left,只需要保证将max_left更改为height[left]时,height[left] < height[right]
要保证左边一定有某个柱子高度大于max_right,只需要保证将max_right更改为height[right]时,height[right] < height[left]

所以,可以尝试着根据height[left]和height[right]大小关系来判断当前是应该从左向右逼近还是从右向左逼近,即

class Solution {
public:int trap(vector<int>& height) {int n = height.size();int ans = 0;int left = 0;int right = n - 1;int max_left = 0;int max_right = 0;while(left <= right){if(height[left] < height[right]){/* 在left右边一定有某个柱子的高度大于max_left,所以较小的就是max_left* 如果当前柱子的高度大于max_left,说明左边没有比它高的柱子,浅蓝色区域块个数为0,此时更新max_left */(max_left < height[left]) ? (max_left = height[left]) : ans += (max_left - height[left]);++left;}else{/* 同理 */(max_right < height[right]) ? (max_right = height[right]) : ans += (max_right - height[right]);--right;}}return ans;}
};

这题主要是弄清楚如何使用简便的方法求出容量,即通过正上方的浅蓝色区域块的个数求解。同时,也需要明白如何求解浅蓝色区域块的个数,即找到左右两边最大的高度中较小的那个和当前柱子高度作差。

每天一道LeetCode-----计算一个直方图空隙的容量(如果装水能装多少)相关推荐

  1. OpenCV2.3的cvCalcHist函数有问题?255级值总为0,索性自己写一个直方图计算函数,附源码

    图像处理开发需求.图像处理接私活挣零花钱,请加微信/QQ 2487872782 图像处理开发资料.图像处理技术交流请加QQ群,群号 271891601 我在写直方图规定化的代码过程中,发现OpenCV ...

  2. 去掉数组最后一个元素_leetcode 34. 在排序数组中查找元素的第一个和最后一个位置每天刷一道leetcode算法系列!...

    作者:reed,一个热爱技术的斜杠青年,程序员面试联合创始人 前文回顾: leetcode1. 两数之和--每天刷一道leetcode系列! leetcode2. 两数相加--每天刷一道leetcod ...

  3. 每天一道LeetCode-----找到一个字符串在另一个字符串出现的位置,字符串内部顺序无要求

    Valid Anagram 原题链接Valid Anagram 实际上就是判断两个字符串中每个字符出现的个数是否相等,直接计算每个字符的个数就可以了. class Solution { public: ...

  4. 每日一道 LeetCode (16):求 x 的平方根

    每天 3 分钟,走上算法的逆袭之路. 前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee ...

  5. 一天一道LeetCode(61-90)

    一天一道LeetCode(61-90) 文章目录 一天一道LeetCode(61-90) 61.旋转链表 62.不同路径 63.不同路径 II 64.最小路径和 65.有效数字(未解决) 66.加一 ...

  6. 每日一道 LeetCode (42):旋转数组

    每天 3 分钟,走上算法的逆袭之路. 前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee ...

  7. 【一天一道Leetcode】基本计算器的延伸问题

    本篇推文共计2000个字,阅读时间约3分钟. 01 题目描述 题目描述: 给你一个字符串表达式s,请你实现一个基本计算器来计算并返回它的值. 整数除法仅保留整数部分. 示例: 输入:s = " ...

  8. 使用Python,OpenCV计算图像直方图(cv2.calcHist)

    使用Python,OpenCV计算图像直方图(cv2.calcHist 1. 效果图 2. 原理 2.1 什么是图像直方图? 2.2 计算直方图 2.3 可视化蒙版区域 3. 源码 参考 这篇博客将介 ...

  9. leetcode17. 电话号码的字母组合--每天刷一道leetcode算法系列!

    作者:reed,一个热爱技术的斜杠青年,程序员面试联合创始人 前文回顾: leetcode1. 两数之和--每天刷一道leetcode系列! leetcode2. 两数相加--每天刷一道leetcod ...

最新文章

  1. C/C++ 误区:fflush(stdin)
  2. linux自动删除30天前的日志文件
  3. 股市投资大师的股市投资名言
  4. python区块链开发_10个开源的Python区块链项目
  5. python3num='0123456789,num「:6:-1」=gt; '987'?「1:6:-1」为空
  6. arduino无源蜂鸣器歌曲编码_Arduino加无源蜂鸣器,播放音乐《葫芦娃》
  7. 《计算机科学概论》—第3章3.3节文本表示法
  8. 数组中的两个常见异常
  9. php eureka客户端,Spring Cloud(一)配置Eureka 服务器(示例代码)
  10. Spring @Aspect实现切面编程
  11. 解决office 2019打开word、excel慢的问题
  12. Bugku:web 秋名山老司机
  13. (附源码)spring boot物联网智能管理平台 毕业设计 211120
  14. VMware上面实现Ubuntu和Windows文件的复制粘贴功能(以及虚拟机当中插入U盘能够显示)
  15. Tomcat 支持的Java版本对照
  16. Python多子图绘制
  17. 全媒体运营师胡耀文教你:从0到1搭建直播运营体系
  18. 埋头工作就能触及 “ 宇宙真理 ”
  19. 阿里p7自己记录整理的一线互联网公司面试(阿里、网易、字节)
  20. linux命令行测网速

热门文章

  1. Java黑皮书课后题第3章:**3.21(科学:某天是星期几)泽勒一致性...编写程序,提示用户输入年、月、该月的哪一天,显示它是一周中的星期几
  2. Java黑皮书课后题第1章:1.1(显示三条消息)编写程序,显示Welcome to Java、Welcome to Computer Science和Programming is fun
  3. C语言学习之求∑n!(即求1!+2!+3!+...+20!)
  4. linux拷贝文件夹怎么删除,linux文件及文件夹拷贝移动删除
  5. Eclipse 上安装STS (springsource-tool-suite)
  6. 类与类之间关系,用C#和JavaScript体现
  7. APScheduler —— Python化的Cron
  8. 【转】使IFRAME在iOS设备上支持滚动
  9. Oracle数据库无法向listener注册的解决一例
  10. 介绍几款好用的Web开发管理工具