LeetCode 42. Trapping Rain Water
解法一:DP
遇到不会的题目,先想想暴力怎么做。对于每个元素,如果用暴力做的话,分别向左向右找最大的值,那么当前元素能装的水为 min(leftmax,rightmax)-a[i]。
leftmax和rightmax都可以记录下来。以leftmax为例,leftmax[i] = max(leftmax[i-1],a[i])。这样的话,每个元素能装的水为 min(leftmax[i],rightmax[i])-a[i]。
class Solution { public: int trap(vector<int>& height) { if (height.size()==0) return 0; int n=height.size(); vector<int> leftmax(n), rightmax(n); leftmax[0] = height[0]; for (int i=1;i<n;++i) leftmax[i] = max(leftmax[i-1],height[i]); rightmax[n-1] = height[n-1]; for (int i=n-2;i>=0;--i) rightmax[i] = max(rightmax[i+1],height[i]); int res=0; for (int i=0;i<n;++i){ res += min(leftmax[i],rightmax[i])-height[i]; } return res; } };
解法二:Two Pointers
思路还是一样的思路,但是这里用left和right两个指针来表示左和右的最大值。一开始分别指向数组的收尾位置,从两边向中间扫描,扫描方向为指向较小元素的指针向较大的指针(因为是min(leftmax,rightmax)-a[i])。如果扫描的元素比较小的指针还小,那么res+= height[小指针]-height[i];否则更新该指针,并重新判断扫描方向,直至left==right为止。
class Solution { public: int trap(vector<int>& height) { if (height.size()==0) return 0; int n=height.size(); int left=0, right=n-1; int leftmax=0, rightmax=0; int res=0; while (left<right){ if (height[left]<height[right]){ if (height[left]>leftmax) leftmax=height[left]; else res+=leftmax-height[left]; ++left; }else{ if (height[right]>rightmax) rightmax=height[right]; else res+=rightmax-height[right]; --right; } } return res; } };
解法三:Stack
stack是最直观的的解法,维护一个递减的单调栈,遇到比top元素小的入栈,遇到比top大的,出栈直到当前元素比top小入栈为止。出栈的元素计算一下加入到结果中。
需要注意的是,这里必须i-left-1,因为因为left,mid,i的index都不一定是连续的。
class Solution { public: int trap(vector<int>& height) { if (height.size()==0) return 0; int res=0; stack<int> s; for (int i=0;i<height.size();++i){ while (!s.empty() && height[i]>height[s.top()]){ int mid=s.top(); s.pop(); if (s.empty()) break; int left=s.top(); res += ( min(height[i],height[left])-height[mid] ) * (i-left-1); } s.push(i); } return res; } };