题目描述:
给定 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
思路分析:
思路一:这是一个类似木桶效应的问题。比较多的思路是利用单调栈来实现。之前大四上算法课的时候也做过类似的题目。维护一个单调递减的栈,当遇到大于当前栈顶的高度时,出栈,计算可接多少水,算法是取现在的栈顶元素与待进栈元素的较小高度减去这个出栈元素的高度,再乘上待进栈元素与栈顶元素的宽度。这里注意维护在栈内的元素时对应的下标,以保证能够求得正确的宽度。这个方法是逐层求可接雨水。时间复杂度为O(n^2)。
思路二:
在实际的面试过程中,肯定是希望能够优化这个算法,考虑用双指针,逐列的来取雨水。维护左右两个指针left和right,分别指向数组的头尾,同时维护两个数值,存maxleft和maxright,即当前遍历过的所有左右元素的最大高度。需要进行判断,若left的高度小于right的高度,则left++,否则right--。同时在每次判断是还需要多一次判断,即是否能够加水,需要判断当left小于right时,left是否小于maxleft,若是,那么就可以加水,所加的水为maxleft-height[left]。反之,当right小于等于left的高度时,添加maxright-height[right]的水量。这个方法的时间复杂度为O(n)。
代码:
思路一:
1 class Solution { 2 public: 3 4 int trap(vector<int>& height) { 5 if(height.size()==0) 6 return 0; 7 int sum_rain = 0; 8 stack<int> s; 9 for(int i=0; i<height.size(); i++) 10 { 11 if(s.empty() || height[i]<height[s.top()]) 12 s.push(i); 13 else 14 { 15 while(!s.empty() && height[i]>height[s.top()]) 16 { 17 int pre = s.top(); 18 s.pop(); 19 if(!s.empty()) 20 { 21 sum_rain += (min(height[i], height[s.top()])-height[pre])*(i-s.top()-1); 22 } 23 } 24 s.push(i); 25 } 26 } 27 return sum_rain; 28 } 29 };
思路二:
1 class Solution { 2 public: 3 int trap(vector<int>& height) { 4 if(height.size()==0) 5 return 0; 6 int sum_rain = 0; 7 int left=0, right = height.size()-1; 8 int leftmax=0, rightmax=0; 9 while(left<right) 10 { 11 if(height[left]<height[right]) 12 { 13 if(height[left]<leftmax) 14 { 15 sum_rain += (leftmax-height[left]); 16 } 17 else 18 leftmax = height[left]; 19 left++; 20 } 21 else 22 { 23 if(height[right]<rightmax) 24 { 25 sum_rain += (rightmax-height[right]); 26 } 27 else 28 rightmax = height[right]; 29 right--; 30 31 } 32 33 } 34 return sum_rain; 35 } 36 };