題目連接:https://leetcode.com/problems/trapping-rain-water/description/app
題目大意:與84題作比較,在直方圖中計算其蓄水能力。例子以下:ide
法一(借鑑):暴力,仍是很難想到的,須要推理數學功底。由於這裏暴力的前提條件是:計算每一個點的蓄水能力,至關於求解,min(每一個點左邊的最大高度,每一個點右邊的最大高度)-當前值。也就是這裏,把一整個蓄水池的分解成一個點一個點的蓄水能力。代碼以下(耗時150ms):spa


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 }
法二(借鑑):一維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 }
法三(借鑑):stack,與84題stack作法比較,此題在壓棧的時候是降序高度壓棧,噹噹前高度>s.peek()時,能夠蓄水,棧頂第一個元素爲坑最低高度,棧頂第二個元素爲左邊界,當前高度爲右邊界,坑深爲min(左高度,右高度)-坑最低高度。代碼以下(耗時30ms):code


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 }
法四(借鑑):兩個指針移動,比較左指針指向的高度和右指針指向的高度,記錄較小者,若是左指針指向的較小,則從左往右遍歷,若是右指針指向的較小,則從右往左遍歷,若是遍歷到的當前值比較小者小,則表示能夠蓄水:較小者-當前值。代碼以下(22ms):blog


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 }