LeetCode.42 接雨水(Trapping Rain Water)(JS)

作有意思的題是要付出代價的,代價就是死活作不出來。

1、題目

接雨水:數組

給定 n 個非負整數表示每一個寬度爲 1 的柱子的高度圖,計算按此排列的柱子,下雨以後能接多少雨水。
圖片描述
上面是由數組 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度圖,在這種狀況下,能夠接 6 個單位的雨水(藍色部分表示雨水)。
示例 1:
輸入: [0,1,0,2,1,0,1,3,2,1,2,1]
輸出: 6

2、個人答案

思路1

       前段時間有在看一個俄羅斯方塊的代碼,因此第一個思路是從下到上一層一層計算,先把數組中全部的數-1,而後去掉數組兩端的-1,再統計數組中-1的個數,即爲本層接的雨水。這種暴力解法應該是可行的,我並無把思路落到紙上,各位感興趣能夠試一下。app

思路2

       接雨水嘛,兩邊比中間高就能接到,那麼我只須要求出全部比左右兩邊高的點,他們兩兩組合就成一個能夠接到水的坑,以原題中的示例1爲例,下標爲1,3,7,10的點就是咱們所求。代碼以下函數

/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
  function fillister (arr) {
    let result = 0
    let baseLine = arr[0] <= arr[arr.length - 1] ? arr[0] : arr[arr.length - 1]
    let difference
    for(let i = 0; i < arr.length; i++) {
      difference = baseLine - arr[i]
      difference > 0 ? result += difference : null
    }
    return result
  }
  let result = 0
  let bigger, smaller, the_dot
  for(let begin = 0; begin < height.length; begin++) {
    bigger = (height[begin - 1] || 0) < height[begin]
    smaller = (height[begin + 1] || 0) < height[begin]
    if (smaller && bigger) {
      if (the_dot !== undefined) {
        result += fillister(height.slice(the_dot, begin + 1))
      }
      the_dot = begin
    }
  }
  return result
};

       遍歷參數數組height,只要符合條件,且不是第一個符合條件的點,就計算該點與以前點之間的積水。提交,答案錯誤。出錯的測試用例爲[5,1,2,1,5]。
       原來按照上述思路,只計算了[5,1,2]和[2,1,5]兩個小水窪,可是[5,1,2,1,5]自己就是個大水窪。意識到計算目標點的函數須要遞歸。
       根據這個想法,我進行了大量的編碼,由於這個遞歸調用的邊界狀況比較麻煩,並且越寫越懷疑本身的思路,我始終以爲優秀的題解應該是簡單的。這裏只放出其中對我產生啓發的一個片斷。測試

function noNeedToCall (arr) {
    let max = Math.max.apply(null, arr)
    let maxIndex = arr.indexOf(max)
    for(let i = 0, len = arr.length - 1; i < len; i++){
      if(i < maxIndex) {
        if(arr[i] > arr[i + 1]) return false
      } else {
        if(arr[i] < arr[i + 1]) return false
      }
    }
    return true
  }

       上面這段代碼的做用在於遞歸函數調用的最後,判斷是否須要繼續遞歸。若是在最大值左邊的數組是遞增或持平,在最大值右邊的數組是遞減或持平的就不須要遞歸,不然繼續調用。也就是說最後求出來是以最大值爲界,左邊一個水窪,右邊一個水窪(槓精:「[2,1,3,1,4,1,2]這個例子中最大值4左邊有兩個水窪,垃圾博主」,)左邊一個水窪集,右邊一個水窪集。編碼

思路3

       雖然左右兩個水窪,可是決定他們範圍的兩個點中的最大值都確定都是整個數組的最大值,也就是說決定他們積水量的值也就是較小值是存在於左右兩邊的,那我爲何要各類調用各類遞歸,直接分左右兩側共循環一遍數組就行了。上代碼spa

/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
  const max = Math.max.apply(null, height)
  const maxIndex = height.indexOf(max)
  let i = 0, temp = 0, result = 0
  for (i = 0; i < maxIndex; i++) {
    if (height[i] >= temp) {
      temp = height[i]
    } else {
      result += temp - height[i]
    }
  }
  temp = 0
  for (i = height.length - 1; i > maxIndex; i--) {
    if (height[i] >= temp) {
      temp = height[i]
    } else {
      result += temp - height[i]
    }
  }
  return result
};

3、優秀答案

/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
    if (!height || !height.length) {
        return 0;
    }
    
    let maxLeftWall = 0;
    let maxRightWall = 0;
    
    let water = 0;
    let i = 0;
    let j = height.length - 1;
    while (i < j) {
        if (height[i] < height[j]) {
            if (height[i] >= maxLeftWall) {
                maxLeftWall = height[i];
            } else {
                water += maxLeftWall - height[i];
            }
            i++;
        } else {
            if (height[j] >= maxRightWall) {
                maxRightWall = height[j];
            } else {
                water += maxRightWall - height[j];
            }
            j--;
        }
    }
    
    return water;
};

       思路是類似的,不過對於處理最大值的方式不一樣,代碼放這兒就不細說了debug

4、路漫漫其修遠兮

       這道題真的難了我很久,從想到求水窪兩端的點,到遞歸調用的處理,到推翻以前的思路左右分別處理。過程當中各類測試用例a經過測試用例b不經過,跟着debugger一點點看而後改而後測試用例b經過測試用例a又不經過。
       最後思路迸發出來寫出來提交經過,這種感受真是爽快,就像是穿着新內褲迎接新年來到的早晨同樣。code

clipboard.png       你不要過來啊!!!遞歸

相關文章
相關標籤/搜索