R
E
A
C
T
O

xamples
epeat
ode
pproach
ptimize
est
{Rainwater Collector}

The Question
You're an industrious programmer that lives off the grid. The local well that you use to fetch water has gone dry, so you've decided to collect rain water to filter. However, your collection device isn't flat. Determine how much rain you could possibly collect at once.
PROBLEM: Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water your collection device is able to trap after raining.

Example
EXAMPLE: Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.

Keep in mind...
- Water will only gather up to the height of its reservoir walls.
- We can therefore think about this problem in terms of height.
- Water cannot be held in the first or last bar, due to no containing wall on one side.

The Approach
-
Instead of summing the volume vertically, we can think about how much volume exists in a horizontal plane at each incremental height.
-
Our solution goes about this by
-
starting at the highest 'peak',
-
summing the total amount of volume at that level,
-
decrementing the height by 1 and
-
repeating the process until a height of 0 is reached.
-

Tests to Run
// vol = 7
var a = [0,0,1,2,4,3,2,5,0,0,2,1]
console.log('collection device "a" can hold', totalVol(a))
// vol = 6
var b = [0,1,0,2,1,0,1,3,2,1,2,1]
console.log('collection device "b" can hold', totalVol(b))
// vol = 12
var c =[0,3,0,1,0,0,0,1,0,2]
console.log('collection device "c" can hold', totalVol(c))
// vol = 8
var d = [0,1,0,3,5,0,0,0,2,0,1]
console.log('collection device "d" can hold', totalVol(d))
// vol = 38
var e = [0,5,3,2,8,8,1,1,2,4,3,3,7,1,2,4,3,2]
console.log('collection device "e" can hold', totalVol(e))
'use strict'
function rainCollector(arr){
var volume = 0;
var maxVal = 0;
arr.forEach(function(elem){
if(elem > maxVal) maxVal = elem;
})
//look at horizontal layers to determine volume
for(let height = maxVal; height>0; height--){
let positions = [];
//find indices of 'peaks' at each height
for(let j = 0; j < arr.length; j++){
if(arr[j] >= height) positions.push(j);
}
//find the volume at each layer
volume = positions.reduce(function(accVol, curr, index){
//the current peak index minus the previous peak index - 1
//e.g. index 3 - index 0 - 1 gives a volume of 2 as expected
if(index > 0) return accVol + (curr - positions[index - 1] - 1);
return acc;
}, volume)
}
return volume;
}
var rainArr = [0,5,3,2,8,8,1,1,2,4,3,3,7,1,2,4,3,2];
console.log(rainCollector(rainArr)); //logs 38

Big O Notation
function rainCollector(arr){
var volume = 0; //space O(1)
var maxVal = 0; //space O(1)
arr.forEach(function(elem){ //time O(n) - 'n' being input array length
if(elem > maxVal) maxVal = elem; //time O(1)
})
for(let height = maxVal; height>0; height--){ //time O(h) - 'h' being height
let positions = []; //space O(n) as a max
for(let j = 0; j < arr.length; j++){ //time O(n)
if(arr[j] >= height) positions.push(j); //time O(1)
}
volume = positions.reduce(function(accVol, curr, index){
if(index > 0) return accVol + (curr - positions[index - 1] - 1);
return acc; //time O(1)
}, volume) //time O(n) max
}
return volume;
}
//Overall (after reducing) -- time O(h*n), space O(n)

Alternate Approach
- Find the highest peaks to the left of each position by rightward scan.
- Similarly, find the highest peaks to the right of each position by leftward scan.
- At each position:
- Take the minimum peak
- Subtract the current height (e.g. if the current height is equal to the peak then there is no volume to add)
- Sum up each position to find total volume
- Time complexity: O(n)
- Space complexity: O(n)

Alternate Approach Implemented
function rainCollector(arr){
var leftMax = [], rightMax = [], max = 0, volume = 0;
//make arrays the same length as the input,
// containing max heights at each index
for(let i = 0; i < arr.length; i++){
max = arr[i] > max ? arr[i] : max;
leftMax[i] = max;
}
max = 0; //reset max
for(let i = arr.length - 1; i >= 0; i--){
max = arr[i] > max ? arr[i] : max;
rightMax[i] = max;
}
//Go through the array summing vertically based on peaks
//Don't forget to subtract the value of the element itself
arr.forEach((el, i) => {
volume += Math.min(leftMax[i], rightMax[i]) - el;
})
return volume;
}

Alternate Approach Optimizated
function rainCollector(arr){
var rightMax = [], max = 0, volume = 0;
for(let i = arr.length - 1; i >= 0; i--){
max = arr[i] > max ? arr[i] : max;
rightMax[i] = max;
}
max = 0;
for(let i = 0; i < arr.length; i++){
max = arr[i] > max ? arr[i] : max;
volume += Math.min(max, rightMax[i]) - arr[i];
}
return volume;
}

Solution with comments:
Rainwater Collector
By Kate Humphrey
Rainwater Collector
Technical interview problem for determining water collected between indices
- 1,732