42.Trapping Rain Water---dp,stack,两指针

题目链接:https://leetcode.com/problems/trapping-rain-water/description/

题目大意:与84题做比较,在直方图中计算其蓄水能力。例子如下:

法一(借鉴):暴力,还是很难想到的,需要推理数学功底。因为这里暴力的前提条件是:计算每个点的蓄水能力,相当于求解,min(每个点左边的最大高度,每个点右边的最大高度)-当前值。也就是这里,把一整个蓄水池的分解成一个点一个点的蓄水能力。代码如下(耗时150ms):

 1     public int trap(int[] height) {
 2         int res = 0;
 3         for(int i = 0; i < height.length; i++) {
 4             int left = height[i], right = height[i];
 5             //找左边最大高度
 6             for(int j = i - 1; j >= 0; j--) {
 7                 left = Math.max(left, height[j]);
 8             }
 9             //找右边最大高度
10             for(int j = i + 1; j < height.length; j++) {
11                 right = Math.max(right, height[j]);
12             }
13             //计算蓄水
14             res += Math.min(left, right) - height[i];
15         }
16         return res;
17     }
View Code

法二(借鉴):一维dp,思想与上面暴力相同,都是取左右两边高度的较小者然后与当前值比较,如果大,则可以蓄水。代码如下(耗时22ms):

 1     public int trap(int[] height) {
 2         int dp[] = new int[height.length];
 3         int ma = 0, res = 0;
 4         //记录i值左边的最大高度
 5         for(int i = 0; i < height.length; i++) {
 6             dp[i] = ma;
 7             //更新左边最大高度
 8             ma = Math.max(ma, height[i]);
 9         }
10         //更新计算i值右边的最大高度
11         ma = 0;
12         for(int i = height.length - 1; i >= 0; i--) {
13             //在左边和右边最大高度中取较小者
14             dp[i] = Math.min(dp[i], ma);
15             //更新右边
16             ma = Math.max(ma, height[i]);
17             //如果两边高度比当前高度高,则表示可以蓄水
18             if(dp[i] > height[i]) {
19                 res += dp[i] - height[i];
20             }
21         }
22         return res;
23     }
View Code

法三(借鉴):stack,与84题stack做法比较,此题在压栈的时候是降序高度压栈,当当前高度>s.peek()时,可以蓄水,栈顶第一个元素为坑最低高度,栈顶第二个元素为左边界,当前高度为右边界,坑深为min(左高度,右高度)-坑最低高度。代码如下(耗时30ms):

 1     //入栈递减高度,当当前高度>s.peek()时,可以蓄水,栈顶第一个元素为坑最低高度,栈顶第二个元素为左边界,当前高度为右边界,坑深为min(左高度,右高度)-坑最低高度
 2     public int trap(int[] height) {
 3         Stack<Integer> s = new Stack<Integer>();
 4         int res = 0;
 5         for(int i = 0; i < height.length; i++) {
 6             //循环查找在当前高度下,是否可以蓄水
 7             while(!s.isEmpty() && height[i] > height[s.peek()]) {
 8                 int cur = s.pop();
 9                 //如果栈空,说明没有左边界,没法蓄水,所以直接break
10                 if(s.isEmpty()) {
11                     break;
12                 }
13                 //计算蓄水,此时不再是按照前面的方法,分点计算竖状蓄水能力,而是按照整块的蓄水能力计算的,也就是普通思维的按照蓄水池的长*宽来计算的
14                 res += (i - s.peek() - 1) * (Math.min(height[s.peek()], height[i]) - height[cur]);
15             }
16             //如果比栈顶高度小,压栈
17             s.push(i);
18         }
19         return res;
20     }
View Code

 法四(借鉴):两个指针移动,比较左指针指向的高度和右指针指向的高度,记录较小者,如果左指针指向的较小,则从左往右遍历,如果右指针指向的较小,则从右往左遍历,如果遍历到的当前值比较小者小,则表示可以蓄水:较小者-当前值。代码如下(22ms):

 1     //定义两个指针,左指针与右指针的值进行比较,记录较小者,如果左指针较小,则从左往右遍历,如果右指针较小,则从右往左遍历,如果遍历到的值比较小者小,则表示可以蓄水:较小者-当前值
 2     public int trap(int[] height) {
 3         int left = 0, right = height.length - 1, mi = 0, res = 0;
 4         while(left < right) {
 5             //如果左指针较小,从左往右遍历
 6             if(height[left] < height[right]) {
 7                 mi = height[left++];
 8                 while(left < right && height[left] < mi) {
 9                     res += mi - height[left++];
10                 }
11             }
12             //如果右指针较小,从右往左遍历
13             else {
14                 mi = height[right--];
15                 while(left < right && height[right] < mi) {
16                     res += mi - height[right--];
17                 }
18             }
19         }
20         return res;
21     }
View Code
原文地址:https://www.cnblogs.com/cing/p/8494152.html