給定 n 個非負整數表示每個寬度爲 1 的柱子的高度圖,計算按此排列的柱子,下雨之後能接多少雨水。
上面是由數組 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度圖,在這種情況下,可以接 6 個單位的雨水(藍色部分表示雨水)。
示例:
輸入: [0,1,0,2,1,0,1,3,2,1,2,1]
輸出: 6
輸入: [2, 0, 2]
輸出: 2
首先,分析一下解題思路:
- 方案一:按照一層一層統計
要領:逐層統計,每層去掉首尾空部分,然後計算中間缺失部分
第一層
取出藍色模塊,2個
第二層
取出藍色模塊,4個
第三層
取出藍色模塊,0個
代碼實現:
import java.util.*;
/**
* @author yanghao
* @version LeetCode42Test.java, v 0.1 2019-10-14 16:13
*/
public class LeetCode42Test {
public static void main(String[] args){
LeetCode42Test test = new LeetCode42Test();
int[] height = new int[]{0,1,0,2,1,0,1,3,2,1,2,1};
//int[] height = new int[]{2, 0, 2};
System.out.println(test.leetCode42(height));
}
public int leetCode42(int[] height) {
//取出最大值
int max = 0;
for(int h : height){
if(h > max){
max = h;
}
}
int start = 0;
int end = 0;
int pool = 0;
int all = 0;
//一層一層計算
for(int storey = 1;storey <= max;storey ++){
for(int site = 0;site < height.length;site ++){
if(height[site] >= storey){
//標記每層開始
if(start == 0){
start = site + 1;
}
//標記每層結束
end = site + 1;
}
if(height[site] < storey){
//標記池子數量
pool ++;
}
}
//計算本層可裝水量 = 總池數 - 開頭池 - 結尾池
all += pool - (start - 1) - (height.length - end);
//每層初始化數據
start = 0;
end = 0;
pool = 0;
}
return all;
}
總結:
此方法可以實現最終的目的,而且代碼邏輯比較常規,但是循環次數多,如果給的數組足夠大和多,代碼效率將會很低下。
- 方案二:自左向右統計
要領1:從左到右開始,如果右邊比左邊高,說明已經儲水(如下2種情況)
計算公式 = 中間水位個數 * 最低水位 - 水底柱佔用空間(開頭0,1可以包含計算)
要領2:如果左邊比右邊高,情況有很多,可能就是無法儲水了,或者存在更低的儲水池(如下3種情況)
如果把中間最高位分割之後,右邊全部倒置,即可和左邊一致計算公式
代碼實現:
import java.util.*;
/**
* @author yanghao
* @version LeetCode42Test.java, v 0.1 2019-10-11 19:13
*/
public class LeetCode42Test {
public static void main(String[] args){
LeetCode42Test test = new LeetCode42Test();
int[] height = new int[]{0,1,0,2,1,0,1,3,2,1,2,1};
//int[] height = new int[]{2, 0, 2};
System.out.println(test.leetCode42(height));
}
public int leetCode42(int[] height) {
//從最高峯,分成2個數組
int max = 0;
int index = 0;
for(int i = 0; i < height.length; i ++){
if(height[i] > max){
max = height[i];
index = i;
}
}
int[] left = new int[index + 1];
System.arraycopy(height, 0, left, 0, index + 1);
int[] right = new int[height.length - index];
System.arraycopy(height, index, right, 0, height.length - index);
//倒置
for(int i = 0;i < right.length/2; i ++){
int temp = right[i];
right[i] = right[right.length - 1 -i];
right[right.length - 1 -i] = temp;
}
//左邊 + 右邊
return getPool(left) + getPool(right);
}
private int getPool(int[] height) {
//記錄左邊位置
int left = 0;
//記錄實心部分
int temp = 0;
//記錄總
int all = 0;
for(int site = 1; site < height.length; site ++){
if(height[site] >= height[left]){
//右邊比左邊高,記錄池水 = 長(剔除兩邊牆) * 寬 - 實心
all += (site - left - 1) * height[left] - temp;
//左邊位置重新標記爲右邊位置
left = site;
//實心數據初始化
temp = 0;
}else{
//左邊比右邊高,記錄實心數據
temp += height[site];
}
}
return all;
}
}
總結:
此方法效率高,代碼執行速度很快,但是理解成本較高,邏輯複雜。
大家可以發揮自己的想象,應該還有更多方式可以寫出來~