题解 | #接雨水问题#
接雨水问题
http://www.nowcoder.com/practice/31c1aed01b394f0b8b7734de0324e00f
一.题目描述
NC128接雨水问题
给定一个整形数组arr,已知其中所有的值都是非负的,将这个数组看作一个柱子高度图,计算按此排列的柱子,下雨之后能接多少雨水。
输入:[3,1,2,5,2,4]
返回:5
二.算法(双指针)
使用两个指针,一个l_max,一个r_max:
首先每次循环开始,先获取l的左边 [0, l-1]最高柱子高度和r右边[r+1,len-1]最高柱子高度(都不包括l和r本身)
(1)当l_max<r_max时,那么就说明对于l右边一定有比l_max更高的柱子,那么只需要判断l左边 最高柱子l_max 是否比l柱子高就行了,如果是,那么就能装水
(2)当l_max>=r_max时,那么就说明对于r左边一定有比r_max更高或者相同高度的柱子,那么只需要判断r右边最高柱子r_max是否比r柱子高就行了
下面是通过代码:
class Solution { public: long long maxWater(vector<int>& arr) { int len=arr.size(); if(len<=2) return 0; int l=0,r=len-1; int m=min(arr[l],arr[r]); long long int sum=0; // 找出左右边界的最小值作为水位高度 while(l<r){ if(arr[l]<arr[r]){ l++; // 如果当前标尺小于水位,则水量累加 if(arr[l]<m){ sum+=m-arr[l]; } else { // 否则,将此标尺和右边边界高度进行比较,找出剩下数组中的新水位 m=min(arr[l],arr[r]); } } else { r--; // 同理,如果当前标尺小于水位,则水量累加 if(arr[r]<m){ sum+=m-arr[r]; } else { // 否则,将此标尺和左边界的高度进行比较,找出剩余数组中的新水位 m=min(arr[r],arr[l]); } } } return sum; } };
时间复杂度:。只遍历了一遍数组。
空间复杂度:。使用了有限的 left, right, ans, maxleft, maxright。
优缺点:空间复杂度低,但是代码实现比较麻烦
三.算法(单调栈)
除了计算并存储两个位置两边的最大高度以外,也可以利用单调栈计算能接到的雨水总量。维护一个单调栈,单调栈存储的下标,满足从栈底到栈底的下标对应的数组height中的元素递减。
下面是完整代码:
class Solution { public: long long maxWater(vector<int>& arr) { long long int ans=0; stack<int>st; int n=arr.size(); for(int i=0;i<n;i++){ while(!st.empty()&&arr[i]>arr[st.top()]){ int top=st.top(); st.pop(); //盛水的水底,用过了就要pop出来 if(st.empty()) break; //栈中元素没有底了 就直接结束 long long int left=st.top(); long long int width=i-left-1; long long int height=min(arr[left], arr[i])-arr[top]; ans+=width*height; } st.push(i); } return ans; } };
时间复杂度:,其中n是数组arr的长度。
空间复杂度:,其中n数组arr的长度。空间复杂度主要取决于栈空间,栈的大小不会超过n。
优缺点:空间复杂度高,但是代码实现简单