Water collected between towers

后端 未结 26 995
无人共我
无人共我 2020-12-22 16:51

I recently came across an interview question asked by Amazon and I am not able to find an optimized algorithm to solve this question:

You are given an input array wh

相关标签:
26条回答
  • 2020-12-22 17:19
    private static int soln1(int[] a)
        {
            int ret=0;
            int l=a.length;
            int st,en=0;
            int h,i,j,k=0;
            int sm;
            for(h=0;h<l;h++)
            {
            for(i=1;i<l;i++)
            {
                if(a[i]<a[i-1])
                {
                    st=i;
                    for(j=i;j<l-1;j++)
                    {
                        if(a[j]<=a[i] && a[j+1]>a[i])
                        {
                            en=j;
                            h=en;
                            break;
                        }
                    }
                    if(st<=en)
                    {
                        sm=a[st-1];
                        if(sm>a[en+1])
                            sm=a[en+1];
                        for(k=st;k<=en;k++)
                        {
                            ret+=sm-a[k];
                            a[k]=sm;
                        }
                    }
                }
            }
            }
            return ret;
        }
    
    0 讨论(0)
  • 2020-12-22 17:20

    I have a solution that only requires a single traversal from left to right.

    def standing_water(heights):
    
        if len(heights) < 3:
            return 0
    
        i = 0   # index used to iterate from left to right
        w = 0   # accumulator for the total amount of water
    
        while i < len(heights) - 1:
    
            target = i + 1
            for j in range(i + 1, len(heights)):
    
                if heights[j] >= heights[i]:
                    target = j
                    break
    
                if heights[j] > heights[target]:
                    target = j
    
            if target == i:
                return w
    
            surface = min(heights[i], heights[target])
    
            i += 1
    
            while i < target:
                w += surface - heights[i]
                i += 1
    
        return w
    
    0 讨论(0)
  • 2020-12-22 17:21

    An intuitive solution for this problem is one in which you bound the problem and fill water based on the height of the left and right bounds.

    My solution:

    • Begin at the left, setting both bounds to be the 0th index.
    • Check and see if there is some kind of a trajectory (If you were to walk on top of these towers, would you ever go down and then back up again?) If that is the case, then you have found a right bound.
    • Now back track and fill the water accordingly (I simply added the water to the array values themselves as it makes the code a little cleaner, but this is obviously not required).
    • The punch line: If the left bounding tower height is greater than the right bounding tower height than you need to increment the right bound. The reason is because you might run into a higher tower and need to fill some more water. However, if the right tower is higher than the left tower then no more water can be added in your current sub-problem. Thus, you move your left bound to the right bound and continue.

    Here is an implementation in C#:

            int[] towers = {1,5,3,7,2};
    
            int currentMinimum = towers[0];
    
            bool rightBoundFound = false;
    
            int i = 0;
            int leftBoundIndex = 0;
            int rightBoundIndex = 0;
    
            int waterAdded = 0;
    
            while(i < towers.Length - 1)
            {
    
                currentMinimum = towers[i];
    
                if(towers[i] < currentMinimum)
                {
                    currentMinimum = towers[i];
                }
    
                if(towers[i + 1] > towers[i])
                {
                    rightBoundFound = true;
                    rightBoundIndex = i + 1;
                }
    
                if (rightBoundFound)
                {
    
                    for(int j = leftBoundIndex + 1; j < rightBoundIndex; j++)
                    {
    
                        int difference = 0;
    
                        if(towers[leftBoundIndex] < towers[rightBoundIndex])
                        {
                            difference = towers[leftBoundIndex] - towers[j];
                        }
                        else if(towers[leftBoundIndex] > towers[rightBoundIndex])
                        {
                            difference = towers[rightBoundIndex] - towers[j];
                        }
                        else
                        {
                            difference = towers[rightBoundIndex] - towers[j];
                        }
    
                        towers[j] += difference;
                        waterAdded += difference;
    
                    }
    
                    if (towers[leftBoundIndex] > towers[rightBoundIndex])
                    {
                        i = leftBoundIndex - 1;
                    }
                    else if (towers[rightBoundIndex] > towers[leftBoundIndex])
                    {
                        leftBoundIndex = rightBoundIndex;
                        i = rightBoundIndex - 1;
                    }
                    else
                    {
                        leftBoundIndex = rightBoundIndex;
                        i = rightBoundIndex - 1;
                    }
                    rightBoundFound = false;
    
                }
    
                i++;
    
            }
    

    I have no doubt that there are more optimal solutions. I am currently working on a single-pass optimization. There is also a very neat stack implementation of this problem, and it uses a similar idea of bounding.

    0 讨论(0)
  • 2020-12-22 17:21

    JavaScript Program for finding store total water:

    let buildingHeights = [6, 1, 3, 5, 9, 2, 8];
    /*
     * TOTAL store water
     * */
    let max = (n1, n2) => {
        return n1 > n2 ? n1 : n2;
    };
    let min = (n1, n2) => {
        return n1 > n2 ? n2 : n1;
    };
    
    let maxHeightFromLeft = {}, maxHeightFromRight = {};
    for (let i = 0; i < buildingHeights.length; i++) {
        maxHeightFromLeft[i] = max(buildingHeights[i], (i != 0) ? maxHeightFromLeft[i - 1] : 0);
    }
    for (let i = buildingHeights.length - 1; i >= 0; i--) {
        maxHeightFromRight[i] = max(buildingHeights[i], i < (buildingHeights.length - 1) ? maxHeightFromRight[i + 1] : 0);
    }
    let totalStorage = 0;
    for (let i = 0; i < buildingHeights.length; i++) {
        totalStorage += min(maxHeightFromLeft[i], maxHeightFromRight[i]) - buildingHeights[i];
    }
    console.log(totalStorage);
    
    0 讨论(0)
  • 2020-12-22 17:23

    Here is my take to the problem, I use a loop to see if the previous towers is bigger than the actual one. If it is then I create another loop to check if the towers coming after the actual one are bigger or equal to the previous tower. If that's the case then I just add all the differences in height between the previous tower and all other towers. If not and if my loop reaches my last object then I simply reverse the array so that the previous tower becomes my last tower and call my method recursively on it. That way I'm certain to find a tower bigger than my new previous tower and will find the correct amount of water collected.

    public class towers {
        public static int waterLevel(int[] i) {
            int totalLevel = 0;
    
            for (int j = 1; j < i.length - 1; j++) {
                if (i[j - 1] > i[j]) {
                    for (int k = j; k < i.length; k++) {
                        if (i[k] >= i[j  - 1]) {
                            for (int l = j; l < k; l++) { 
                                totalLevel += (i[j - 1] - i[l]);
                            }
    
                            j = k;
                            break;
                        }  
    
                        if (k == i.length - 1) {
                            int[] copy = Arrays.copyOfRange(i, j - 1, k + 1);
                            int[] revcopy = reverse(copy);
                            totalLevel += waterLevel(revcopy);
                        }
                    }
                }
            }
    
            return totalLevel;
        }
    
        public static int[] reverse(int[] i) {
            for (int j = 0; j < i.length / 2; j++) {
                int temp = i[j];
                i[j] = i[i.length - j - 1];
                i[i.length - j - 1] = temp;
            }
    
            return i;
        }
    
        public static void main(String[] args) {
            System.out.println(waterLevel(new int[] {1, 6, 3, 2, 2, 6}));
        }
    }
    
    0 讨论(0)
  • 2020-12-22 17:25

    Here's an efficient solution in Haskell

    rainfall :: [Int] -> Int
    rainfall xs = sum (zipWith (-) mins xs)
        where mins = zipWith min maxl maxr
              maxl = scanl1 max xs
              maxr = scanr1 max xs
    

    it uses the same two-pass scan algorithm mentioned in the other answers.

    0 讨论(0)
提交回复
热议问题