题目描述 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1] 输出: 6
解法1:两次遍历 思路:
1.第一次遍历找到最高点的位置k
2.第二次先从0到k遍历,记录当前能接住的雨水和sum,
1)首先sum-当前柱子值,然后如果当前值是从0到k最高的柱子则sum+=(height-max)*(k-i)即当前位置到最高柱子能存的雨水数
3.同样从n-1到k遍历,与第二部相似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class Solution {public : int trap (vector<int >& height) { int n=height.size (); if (n<=2 ) return 0 ; int i; int max=-1 ; int k; for (i=0 ;i<n;i++) { if (max<height[i]) { max=height[i]; k=i; } } max=0 ; int sum=0 ; for (i=0 ;i<k;i++) { sum-=height[i]; if (height[i]>max) { sum+=(height[i]-max)*(k-i); max=height[i]; } } max=0 ; for (i=n-1 ;i>k;i--) { sum-=height[i]; if (height[i]>max) { sum+=(height[i]-max)*(i-k); max=height[i]; } } return sum; } };
时间复杂度:O(n)
空间复杂度:O(1)
解法2:动态规划 思路:
1.对于每一列我们求它左边最高的墙,和它右边最高的墙,我们可以用两个数组max_left[i]表示第i列左边墙最高墙的高度,max_right[i]表示右边最高墙的高度,那么i位置能存的水就是则可以这些写:
mins=min(max_left[i],max_right[i]);if(mins>height[i]) i位置的水为mins-height[i];
对应max_left我们可以这样求max_left[i]=max(max_left[i-1],height[i-1])。它前边的墙左边的最高墙的高度,和它前边的墙的高度取一个较大值,同理max_right[i]=max(max_left[i+1],height[i+1])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution {public : int trap (vector<int >& height) { int sum=0 ; int n=height.size (); vector<int > max_left (n,0 ) ; vector<int > max_right (n,0 ) ; int i; for (i=1 ;i<n-1 ;i++) { max_left[i]=max (max_left[i-1 ],height[i-1 ]); } for (i=n-2 ;i>0 ;i--) { max_right[i]=max (max_right[i+1 ],height[i+1 ]); } for (i=1 ;i<n-1 ;i++) { int mins=min (max_left[i],max_right[i]); if (mins>height[i]) sum+=mins-height[i]; } return sum; } };
时间复杂度:O(n)
空间复杂度:O(n)
解法3:双指针 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Solution {public : int trap (vector<int >& height) { int sum = 0 ; int max_left = 0 ; int max_right = 0 ; int left = 1 ; int n=height.size (); int right =n- 2 ; for (int i = 1 ; i < n - 1 ; i++) { if (height[left - 1 ] < height[right + 1 ]) { max_left =max (max_left, height[left - 1 ]); int mins = max_left; if (mins > height[left]) { sum = sum + (mins - height[left]); } left++; } else { max_right =max (max_right, height[right + 1 ]); int mins = max_right; if (mins > height[right]) { sum = sum + (mins - height[right]); } right--; } } return sum; } };
时间复杂度:O(n)
空间复杂度:O(1)
解法4:栈 说到栈,我们肯定会想到括号匹配了。我们仔细观察蓝色的部分,可以和括号匹配类比下。每次匹配出一对括号(找到对应的一堵墙),就计算这两堵墙中的水。
我们用栈保存每堵墙。
当遍历墙的高度的时候,如果当前高度小于栈顶的墙高度,说明这里会有积水,我们将墙的高度的下标入栈。
如果当前高度大于栈顶的墙的高度,说明之前的积水到这里停下,我们可以计算下有多少积水了。计算完,就把当前的墙继续入栈,作为新的积水的墙。
总体的原则就是,
当前高度小于等于栈顶高度,入栈,指针后移。
当前高度大于栈顶高度,出栈,计算出当前墙和栈顶的墙之间水的多少,然后计算当前的高度和新栈的高度的关系,重复第 2 步。直到当前墙的高度不大于栈顶高度或者栈空,然后把当前墙入栈,指针后移。
作者:windliang 链接:https://leetcode-cn.com/problems/trapping-rain-water/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-8/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution {public : int trap (vector<int >& height) { int sum=0 ; int n=height.size (); stack<int > st; int current=0 ; while (current<n) { while (!st.empty ()&&height[current]>height[st.top ()]) { int h=height[st.top ()]; st.pop (); if (st.empty ()) { break ; } int distance=current-st.top ()-1 ; int mins=min (height[current],height[st.top ()]); sum+=(mins-h)*distance; } st.push (current); current++; } return sum; } };
时间复杂度:O(n)
空间复杂度:O(n)