leetcode#42 Trapping rain water

这道题十分有意思,可以用很多方法做出来,每种方法的思想都值得让人细细体会。

42. Trapping Rain Water
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

For example,
Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.

Solution 1:

通过分别计算每一坐标i上有多少水,进而将其相加得到答案。

问题是我们如何知道每一坐标i上有多少水呢?仔细思考,其实只有出现“两高夹一矮”才可能会存到水,如下图所示。

进而,我们可以想到:每一坐标i上存多少水是由 1.其自身高度 2.它左边的最高高度left_most 3.它右边的最高高度right_most三种因素决定的。

当 min{ left_most, right_most} 小于或等于其自身高度时,它能存的水就是0,比如array[1]=1,其left_most= array[0]=0, 其right_most=array[7]=3, min{left_most, right_most}=left_most=0< height= array[1]=1,这也就是说坐标1 存不了水。

当min{ left_most,right_most} 大于其自身高度时,这时这三者间出现了“两高夹一矮”的情况,故其能存水,而且其存水数= min{left_most,right_most} - height。

我们分别来对一些坐标进行验证:

坐标1,存水数=0.//正确

坐标2,leff_most=1,right_most=3,存水数=left_most-height=1-0=1.//正确

坐标3,left_most=1,right_most=3,min{left_most,right_most}=1=height,存水数=0.//正确

读者可以对每个坐标进行验证,会发现以上结论皆是正确的。所以,现在我们的solution就出来了,我们只需要求出每个坐标对应的left_most和right_most,再把存水数相加,就是总的存水数了。

所以,很朴素自然的一个想法就是,遍历一遍数组,对每个数组元素遍历左边一次求出left_most,遍历右边一次求出right_most。

代码如下,

 
//29ms 6.36%
//complexity: O(N^2)
int trap(vector<int>& height)
{
int ans = 0;
int size = height.size();
for (int i = 1; i < size - 1; i++) {
int max_left = 0, max_right = 0;
for (int j = i; j >= 0; j--) { //Search the left part for max bar size
max_left = max(max_left, height[j]);
}
for (int j = i; j < size; j++) { //Search the right part for max bar size
max_right = max(max_right, height[j]);
}
ans += min(max_left, max_right) - height[i];
}
return ans;
}

Solution 2:

在solution 1里,我们已经知道只要求出left_most和right_most,就可以求出答案,那能不能优化一下求这两个数的过程呢?当然是可以的,我们只需要左遍历一次数组,右遍历一次数组,即可得到left_most和right_most。

/*Solution2: 上一种方法其实有优化的空间
通过两次for循环可分别求得left_most和right_most,第三次for循环即可求得sum,
complexity: O(n)
*/
int trap(vector<int>& height)
{
if(height == null)
return ;
int ans = ;
int size = height.size();
vector<int> left_max(size), right_max(size);
left_max[] = height[];
for (int i = ; i < size; i++) {
left_max[i] = max(height[i], left_max[i - ]);
}
right_max[size - ] = height[size - ];
for (int i = size - ; i >= ; i--) {
right_max[i] = max(height[i], right_max[i + ]);
}
for (int i = ; i < size - ; i++) {
ans += min(left_max[i], right_max[i]) - height[i];
}
return ans;
}

Solution 3:

这里再介绍一种优化方法,双指针法,在数组首尾分别创建一个指针,两指针相见时结束循环。

int trap(vector<int>& height)
{
int left = , right = height.size() - ;
int ans = ;
int left_max = , right_max = ;
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;
}

Solution 4:

既然可以纵向的求存水数,那我们能不能一层一层的求存水数呢?

这是第一层,当我们遇到一个空的,且不在边界,存水数+1,所以第一层我们在i=2,i=5 时分别+1.

第二层,存水数+4,依次类推,最终可以求出答案。

代码笔者就不给了,读者有兴趣的可以自己写来试试。

Soluton 5:

这是在leetcode中solution给出的一种很新颖的解法,利用了栈的结构,通过维护一个非递增栈来得到答案。

本质思想还是利用了要存水必须是“两高夹一矮”这个特点,只不过这里是用非递增栈来实现。

下面定义一些符号以便理解:

stack[-1] 栈顶元素

stack[-2] 栈顶的下面一个元素(即倒数第二个元素)

solution4的整个算法是这么实现的:遍历数组,遇到一个元素时,将其与栈顶元素比较,如果其小于等于栈顶元素,直接压栈,将其放入栈中(为维护非递增栈的结构,不能将比栈顶元素大的元素压栈),

若是其大于栈顶元素,此时一定形成了一个“两高夹一矮”局面,因为栈是非递增栈,所以 stack[-1]<stack[-2],又 current>stack[-1],所以是一个“两高夹一矮”局面,此时算完存水数后栈顶元素出栈,继续判断,

递归处理即可。

在上例中整个过程是这样的。

step0: 0不入栈

step1: 1>0 array[1] 入栈 栈:[1]

step2: 0<stack[-1]=1 入栈 栈:[1,0]

step3: 2>stack[-1]=0 存水数+1,0出栈,2>stack[-1]=1, 此时stack内元素不足2,不足以形成“两高夹一矮”局面, 1出栈,2入栈 栈:[2]

step4: 1<stack[-1]=2 1入栈 栈:[2,1]

step5: 0<stack[-1]=1 0入栈 栈:[2,1,0]

step6: 1>stack[-1]=0 存水数+1,0出栈 1=stack[-1] 1入栈 栈:[2,1,1]

step7: 3>stack[-1]=1 存水数+0,1出栈 3>stack[-1]=1 存水数+3,1出栈 3>stack[-1]=2 存水数+0 2出栈 3入栈 栈:[3]

step8: 2<stack[-1] 2入栈 栈:[3,2]

step9: 1<stack[-1] 1入栈 栈:[3,2,1]

step10: 2>stack[-1] 存水数+1 1出栈 2入栈 栈:[3,2,2]

step 11:1<stack[-1] 入栈 栈:[3,2,2,1]

done

/*Solution4
Stack solution
这个solution利用了栈结构,通过维护一个非递增栈,一步一步算出ans
*/ int trap(vector<int>& height)
{
int ans = , current = ;
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() - ;
int bounded_height = min(height[current], height[st.top()]) - height[top];
ans += distance * bounded_height;
}
st.push(current++);
}
return ans;
}

leetcode#42 Trapping rain water的五种解法详解的更多相关文章

  1. LeetCode 42. Trapping Rain Water 【两种解法】(python排序遍历,C++ STL map存索引,时间复杂度O(nlogn))

    LeetCode 42. Trapping Rain Water Python解法 解题思路: 本思路需找到最高点左右遍历,时间复杂度O(nlogn),以下为向左遍历的过程. 将每一个点的高度和索引存 ...

  2. [array] leetcode - 42. Trapping Rain Water - Hard

    leetcode - 42. Trapping Rain Water - Hard descrition Given n non-negative integers representing an e ...

  3. LeetCode - 42. Trapping Rain Water

    42. Trapping Rain Water Problem's Link ------------------------------------------------------------- ...

  4. [LeetCode] 42. Trapping Rain Water 收集雨水

    Given n non-negative integers representing an elevation map where the width of each bar is 1, comput ...

  5. leetCode 42.Trapping Rain Water(凹槽的雨水) 解题思路和方法

    Trapping Rain Water Given n non-negative integers representing an elevation map where the width of e ...

  6. [LeetCode] 42. Trapping Rain Water 解题思路

    Given n non-negative integers representing an elevation map where the width of each bar is 1, comput ...

  7. [leetcode]42. Trapping Rain Water雨水积水问题

    Given n non-negative integers representing an elevation map where the width of each bar is 1, comput ...

  8. LeetCode 42 Trapping Rain Water(积水体积)

    题目链接: https://leetcode.com/problems/trapping-rain-water/?tab=Description   Problem: 根据所给数组的值,按照上图的示意 ...

  9. Java [Leetcode 42]Trapping Rain Water

    题目描述: Given n non-negative integers representing an elevation map where the width of each bar is 1, ...

随机推荐

  1. 【持续更新】.Net 开发中给自己埋下的坑!

    1.文件“XXX”正在由另一进程使用,因此该进程无法访问此文件. 原因剖析:文件在主线程操作,在子线程中读写操作文件,刚开始没有意识到程序的问题所在,总是在FileStream中报错,google后常 ...

  2. (转)MySQL存储过程/存储过程与自定义函数的区别

    转自:http://www.cnblogs.com/caoruiy/p/4486249.html 语法: 创建存储过程: CREATE [definer = {user|current_user}]  ...

  3. PHP异常处理机制

    1. 异常: 异常(Exception)用于在指定的错误发生时改变脚本的正常流程. 当异常被触发时,通常会发生: (1)当前代码状态被保存: (2)代码执行被切换到预定义的异常处理器函数: (3)根据 ...

  4. LeetCode 26. Remove Duplicates from Sorted Array (从有序序列里移除重复项)

    Given a sorted array, remove the duplicates in place such that each element appear only once and ret ...

  5. CentOs 系统启动流程相关

    CentOS的启动流程 1)加载BIOS 的硬件信息,获取第一个启动设备 2)读取第一个启动设备MBR 的引导加载程序(grub) 的启动信息 3)加载核心操作系统的核心信息,核心开始解压缩,并尝试驱 ...

  6. Asp.net MVC4高级编程学习笔记-模型学习第五课MVC表单和HTML辅助方法20171101

    MVC表单和HTML辅助方法 一.表单的使用. 表单中的action与method特性.Action表示表单要提交往那里,因此这里就有一个URL.这个URL可以是相对或绝对地址.表单默认的method ...

  7. 【hihoCoder】#1039 : 字符消除 by C solution

    #1039 : 字符消除 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi最近在玩一个字符消除游戏.给定一个只包含大写字母"ABC"的字符串s,消 ...

  8. C++中的endl

    从开始接触C++到现在,一直以为语句 cout << "hello world!" << endl; 中的endl只是一个相当于C中的换行'\n':直到今天 ...

  9. 【JavaEE】企业面试问题-Java基础

    1. Java基础部分   1.1 Java中的方法覆盖(Overwrite)和方法重载(Overloading)是什么意思? 重载Overload表示同一个类中可以有多个名称相同的方法,但这些方法的 ...

  10. Ignatius and the Princess IV(乱搞一发竟然过了)

    B - Ignatius and the Princess IV Time Limit:1000MS     Memory Limit:32767KB     64bit IO Format:%I64 ...