42.Trapping Rain Water---dp,stack,兩指針

題目連接: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     }
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):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     }
View Code

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