題目鏈接
42. 接雨水
題目描述
解題思路
暴力法(按列求取雨水值)
因爲首尾元素不可能存在雨水,所以可以不用考慮首尾元素。
對於剩餘的每個元素,分別計算從該元素開始,從左和從右開始開始的最大值,然後當前元素對應的能夠接的雨水值 = Math.min(maxLeft,maxRight) - height[i];
時間複雜度:O(n2)
空間複雜度:O(1)
動態規劃
在暴力法中,我們每次都要計算當前座標對應的左邊最大值maxLeft以及右邊最大值maxRight,所以直接導致時間複雜度爲O(n2),然而這些值都是固定的,所以我們可以提前利用兩個數組maxleft[]以及maxRight[]記錄每個下標對應的左邊最大值和右邊最大值。
時間複雜度:O(n)
空間複雜度:O(n)
單調棧
單調遞減棧,棧中存放的是索引值,因爲通過索引值也可以得到height數組中的元素值。
如果當前索引對應的height數組值 <= 前一個索引對應的height數組值,那麼接不雨水。
如果當前索引對應的height數組值 > 前一個索引對應的height數組值,那麼纔有可能接到雨水(注意是有可能,而不是一定,只有同時滿足單調棧不爲空的時候才能接到雨水)
參考下圖就一目瞭然,利用單調棧也可以理解爲是按行計算雨水值
如果當前下標對應的height數組值小於棧頂元素則入棧。
如果當前下標對應的height數組值大於棧頂元素且當前stack不爲空,則說明此時這種情況存在雨水值,雨水值的計算方式如下:
- 首先記錄value = 彈棧元素對應的height數組值
- 其次記錄Math.min(新的棧頂元素對應的height數組值,以及當前索引對應的height數組值),然後減去value值,則爲水坑對應的高度。
- 水坑的寬度 = 當前索引 - 新的棧頂元素。
AC代碼
暴力法
class Solution {
//按照每列的雨水數目計算結果
public int trap(int[] height) {
int ans = 0;
//因爲開頭結尾不會有雨水,所以可以不用考慮
for(int i = 1; i < height.length - 1; i++){
int maxLeft = height[i];
int maxRight = height[i];
for(int j = i - 1; j >= 0; j--){
if(height[j] > maxLeft){
maxLeft = height[j];
}
}
for(int j = i + 1; j < height.length; j++){
if(height[j] > maxRight){
maxRight = height[j];
}
}
ans += Math.min(maxLeft,maxRight) - height[i];
}
return ans;
}
}
動態規劃
class Solution {
public int trap(int[] height) {
if(height.length == 0) return 0;
int maxLeft[] = new int[height.length];
int maxRight[] = new int[height.length];
int ans = 0;
maxLeft[0] = 0;
for(int i = 1; i < height.length; i++){
maxLeft[i] = Math.max(height[i - 1],maxLeft[i-1]);
}
maxRight[height.length-1] = 0;
for(int i = height.length - 2; i >= 0; i--){
maxRight[i] = Math.max(height[i+1],maxRight[i+1]);
}
for(int i = 1; i < height.length; i++){
int h = Math.min(maxLeft[i],maxRight[i]);
if(h > height[i]){
ans += h - height[i];
}
}
return ans;
}
}
//改進版動態規劃
class Solution {
public int trap(int[] height) {
if(height.length == 0) return 0;
int maxRight[] = new int[height.length];
int ans = 0;
int maxLeft = 0;
maxRight[height.length-1] = 0;
for(int i = height.length - 2; i >= 0; i--){
maxRight[i] = Math.max(height[i+1], maxRight[i+1]);
}
for(int i = 1; i < height.length; i++){
maxLeft = Math.max(maxLeft,height[i - 1]);
int h = Math.min(maxLeft,maxRight[i]);
if(h > height[i]){
ans += h - height[i];
}
}
return ans;
}
}
//改進版,雙指針法
public int trap(int[] height) {
int sum = 0;
int max_left = 0;
int max_right = 0;
int left = 1;
int right = height.length - 2; // 加右指針進去
for (int i = 1; i < height.length - 1; i++) {
//從左到右更
if (height[left - 1] < height[right + 1]) {
max_left = Math.max(max_left, height[left - 1]);
int min = max_left;
if (min > height[left]) {
sum = sum + (min - height[left]);
}
left++;
//從右到左更
} else {
max_right = Math.max(max_right, height[right + 1]);
int min = max_right;
if (min > height[right]) {
sum = sum + (min - height[right]);
}
right--;
}
}
return sum;
}
作者:windliang
鏈接:https://leetcode-cn.com/problems/trapping-rain-water/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-8/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
單調棧
class Solution {
public int trap(int[] height) {
Stack<Integer> st = new Stack<>();
int ans = 0;
for(int i = 0; i < height.length; i++){
while(!st.isEmpty() && height[i] > height[st.peek()]){
int index = st.pop();
int value = height[index];
if(st.isEmpty() == false) ans += (Math.min(height[st.peek()],height[i]) - value) * (i-st.peek()-1);
}
st.push(i);
}
return ans;
}
}