— перебор, динамическое программирование, два указателя

Вопрос

  • Индекс может задерживать воду только при наличии полос (более высоких значений) с обеих сторон, поэтому первый и последний индекс никогда не могут задерживать воду.
  • От нижней планки зависит, сколько воды может быть уловлено.

Грубая сила

Так как первый и последний индекс никогда не могут задерживать воду, мы можем перебирать массив от индекса 1 до индекса arr.length-2.

По каждому индексу current мы проверяем левую часть индекса current от индекса 0 до индекса current-1, чтобы найти максимальное значение leftMax.

И мы проверяем правую часть текущего индекса от индекса current+1 до индекса arr.length-1, чтобы найти максимальное значение rightMax.

Затем мы выбираем меньшее значение между leftMax и rightMax минус текущее значение. Если разница больше 0, мы можем добавить ее к общему количеству воды.

Код

var trap = function(height) {
    //start from index one to index height.length-2
    //first and last index cannot trap water
   
    let totalWater = 0
    
    for(let cur = 1; cur < height.length-1; cur++){
        //check left side of current index, and find the max value
        let leftMax = 0
        for(let i = cur-1; i >= 0; i--){
           leftMax = height[i]>leftMax ? height[i]:leftMax
        }
        
         //check right side of current index, and find the max value
        let rightMax = 0
        for(let i = cur+1; i < height.length; i++){
            rightMax = height[i]>rightMax? height[i]:rightMax
        }
        //calculate water
        let water = Math.min(leftMax, rightMax) - height[cur]
        totalWater += water >0 ? water:0
    }
    
    return totalWater
};

Временная сложность: O(n²). Для каждого элемента массива мы итерируем левую и правую части.

Пространственная сложность: O(1).

Протестировать:

 height  [0,1,0,2,1,0,1,3,2,1,2,1]
 index      1 2 3 4 5 6 7 8 9 10

 leftMax    0 1 1 2 2 2 2 3 3 3
 rightMax   3 3 3 3 3 3 3 2 2 1
 min bar    0 1 1 2 2 2 2 2 2 1
-valueOfInd 1 0 2 1 0 1 3 2 1 2 
 water      0 1 0 1 2 1 0 0 1 0 //if diff <0, water is 0
 totalWater = 6           

Динамическое программирование

Путем перебора мы выяснили, что нам нужно знать значения leftMax и rightMax для всех индексов. Мы можем использовать динамическое программирование и хранить их в массивах. Мы зададим два массива: leftMax и rightMax.

Во-первых, мы устанавливаем первому элементу в массиве leftMax первое значение в height для последующего сравнения. Затем мы можем перебрать height (слева направо), сравнивая height[i-1] и leftMax[i-1], чтобы найти значение leftMax для индекса i. Аналогично для массива rightMax, но начинаем с индекса height.length-1 (справа налево).

Давайте сначала посмотрим на некоторые тесты, которые помогут нам понять:

Протестировать

 index      0  1  2  3  4  5  6  7  8  9  10 11
 height    [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
           -->
 leftMax   [0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3]
                                            <--
 rightMax  [3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1]
 min bar    0  0  1  1  2  2  2  2  2  2  1  1
-valueOfInd 0  1  0  2  1  0  1  3  2  1  2  1
 water      0  0  1  0  1  2  1  0  0  1  0  0//if diff<0, water is 0
totalWater = 6

Код

var trap = function(height) {
//use two arrays to track left and right max for each index
    let leftMax = []
    let rightMax = []
    let totalWater = 0
    //set first element of leftMax equals to first element in height for comparison later
    leftMax[0] = height[0]
    //same reason for the rightMax
    rightMax[height.length-1] = height[height.length-1]
    
    //fill the leftMax array
    for(let i=1; i<height.length; i++ ){
        leftMax[i] = leftMax[i-1]>height[i-1] ? leftMax[i-1] : height[i-1]
    }
    
    //fill the rightMax array
    for(let i = height.length-2; i>=0; i--){
        rightMax[i] = rightMax[i+1] > height[i+1]? rightMax[i+1] : height[i+1]
    }
//find the min of leftMax and rightMax for each index, and calculate the water amount
    for(let i = 0 ; i < height.length; i++){
        let water = Math.min(leftMax[i],rightMax[i]) - height[i]
        totalWater+= water > 0 ? water : 0
    }
    
    return totalWater
}

Временная сложность: O(n).
Пространственная сложность: O(n).

Два указателя

Из предыдущего анализа мы знаем, что нижняя полоса определяет, сколько воды может уловить индекс. Итак, у нас есть только два указателя. Один старт слева l, другой справа r. Нам также понадобятся две переменные для хранения максимального значения слева leftMax и справа rightMax. Когда leftMax меньше, чем rightMax , мы знаем, что можем использовать leftMax для определения воды, которая может быть захвачена с правой стороны индекса leftMax.

Логическая прогулка

while( l<=r ){
if leftMax < rightMax 
     if leftMax > height[l] calculate the water
     else leftMax = height[l] //update leftMax
    
     l++ //increment l
else //leftMax >= rightMax, rightMax is the lower bar
     if rightMax > height[l] calculate the water
     else rightMax = height[r] //update rightMax
     r-- //decrease r
}
return total water

Код

var trap = function(height) {
  let l = 0
  let r = height.length - 1
  
  let leftMax = height[l]
  let rightMax = height[r]
  
  let totalWater = 0
  
  while(l<=r){
      if(leftMax<rightMax){
          if(leftMax > height[l]){
              totalWater+= leftMax - height[l]
          }else{
              leftMax = height[l]
          }
          l++
      }else{
          if(rightMax > height[r]){
              totalWater+= rightMax - height[r]
          }else{
              rightMax = height[r]
          }
          r--
      }
      
  }
    
    return totalWater
};

Протестировать

 index      0  1  2  3  4  5  6  7  8  9  10 11
 height    [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
l leftMax  r  rightMax leftMax<rightMax totalWater
0    0    11     1          true            0
1    0    11     1          true            0
2    1    11     1          false           0
2    1    10     1          false           0
2    1    9      2          true            1
3    1    9      2          true            1
4    2    9      2          false           2
4    2    8      2          false           2
4    2    7      2          false           2
4    2    6      3          true            3
5    2    6      3          true            5
6    2    6      3          true            6
7    2    6      3          true            6

Временная сложность: O(n).
Пространственная сложность: O(1).