Water collected between towers

后端 未结 26 994
无人共我
无人共我 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:25

    None of the 17 answers already posted are really time-optimal.

    For a single processor, a 2 sweep (left->right, followed by a right->left summation) is optimal, as many people have pointed out, but using many processors, it is possible to complete this task in O(log n) time. There are many ways to do this, so I'll explain one that is fairly close to the sequential algorithm.

    Max-cached tree O(log n)

    1: Create a binary tree of all towers such that each node contains the height of the highest tower in any of its children. Since the two leaves of any node can be computed independently, this can be done in O(log n) time with n cpu's.

    2a: Then, for each node in the tree, starting at the root, let the right leaf have the value max(left, self, right). This will create the left-to-right monotonic sweep in O(log n) time, using n cpu's.

    2b: To compute the right-to-left sweep, we do the same procedure as before. Starting with root of the max-cached tree, let the left leaf have the value max(left, self, right). These left-to-right (2a) and right-to-left (2b) sweeps can be done in parallel if you'd like to. They both use the max-cached tree as input, and generate one new tree each (or sets their own fields in original tree, if you prefer that).

    3: Then, for each tower, the amount of water on it is min(ltr, rtl) - towerHeight, where ltr is the value for that tower in the left-to-right monotonic sweep we did before, i.e. the maximum height of any tower to the left of us (including ourselves1), and rtl is the same for the right-to-left sweep.

    4: Simply sum this up using a tree in O(log n) time using n cpu's, and we're done.


    1 If the current tower is taller than all towers to the left of us, or taller than all towers to the the right of us, min(ltr, rtl) - towerHeight is zero.

    Here's two other ways to do it.

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

    I wrote this relying on some of the ideas above in this thread:

    def get_collected_rain(towers):
    
        length = len(towers)
        acummulated_water=[0]*length
        left_max=[0]*length
        right_max=[0]*length
    
        for n in range(0,length):
    
            #first left item
            if n!=0:
                left_max[n]=max(towers[:n])
    
            #first right item
            if n!=length-1:
                right_max[n]=max(towers[n+1:length])
    
            acummulated_water[n]=max(min(left_max[n], right_max[n]) - towers[n], 0)
    
        return sum(acummulated_water)
    

    Well ...

    > print(get_collected_rain([9,8,7,8,9,5,6]))

    > 5

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

    Here is my solution, it passes this level and pretty fast, easy to understand The idea is very simple: first, you figure out the maximum of the heights (it could be multiple maximum), then you chop the landscape into 3 parts, from the beginning to the left most maximum heights, between the left most max to the right most max, and from the right most max to the end.

    In the middle part, it's easy to collect the rains, one for loop does that. Then for the first part, you keep on updating the current max height that is less than the max height of the landscape. one loop does that. Then for the third part, you reverse what you have done to the first part

    def answer(heights):
        sumL = 0
        sumM = 0
        sumR = 0
        L = len(heights)
        MV = max(heights)
        FI = heights.index(MV)
        LI = L - heights[::-1].index(MV) - 1
        if LI-FI>1:
            for i in range(FI+1,LI):
                sumM = sumM + MV-heights[i]
    
        if FI>0:
            TM = heights[0]
            for i in range(1,FI):
                if heights[i]<= TM:
                    sumL = sumL + TM-heights[i]
                else:
                    TM = heights[i]
        if LI<(L-1):
            TM = heights[-1]
            for i in range(L-1,LI,-1):
                if heights[i]<= TM:
                   sumL = sumL + TM-heights[i]
                else:
                   TM = heights[i]
        return(sumL+sumM+sumR)        
    
    0 讨论(0)
  • 2020-12-22 17:26

    Here's my attempt in jQuery. It only scans to the right.

    Working fiddle (with helpful logging)

    var a = [1, 5, 3, 7, 2];
    var water = 0;
    
    $.each(a, function (key, i) {
      if (i > a[key + 1]) { //if next tower to right is bigger
          for (j = 1; j <= a.length - key; j++) { //number of remaining towers to the right
              if (a[key+1 + j] >= i) { //if any tower to the right is bigger
                  for (k = 1; k < 1+j; k++) {
                  //add to water: the difference of the first tower and each tower between the first tower and its bigger tower
                      water += a[key] - a[key+k];
                  }
              }
          }
      }
    });
    
    console.log("Water: "+water);
    
    0 讨论(0)
  • 2020-12-22 17:26
    /*** Theta(n) Time COmplexity ***/
            static int trappingRainWater(int ar[],int n)
            {
                int res=0;
                int lmaxArray[]=new int[n];
                int rmaxArray[]=new int[n];
                lmaxArray[0]=ar[0];
                for(int j=1;j<n;j++)
                {
                    lmaxArray[j]=Math.max(lmaxArray[j-1], ar[j]);
                }
                rmaxArray[n-1]=ar[n-1];
                for(int j=n-2;j>=0;j--)
                {
                    rmaxArray[j]=Math.max(rmaxArray[j+1], ar[j]);
                }
                for(int i=1;i<n-1;i++)
                {
                    res=res+(Math.min(lmaxArray[i], rmaxArray[i])-ar[i]);
                }
                return res;
            }
    
    0 讨论(0)
  • 2020-12-22 17:28

    This is a funny problem, I just got that question in an interview. LOL I broke my mind on that stupid problem, and found a solution which need one pass (but clearly non-continuous). (and in fact you even not loop over the entire data, as you bypass the boundary...)

    So the idea is. You start from the side with the lowest tower (which is now the reference). You directly add the content of the towers, and if you reach a tower which is highest than the reference, you call the function recursively (with side to be reset). Not trivial to explain with words, the code speak for himself.

      #include <iostream>
      using namespace std;
    
      int compute_water(int * array, int index_min, int index_max)
      {
      int water = 0;
      int dir;
      int start,end;
      int steps = std::abs(index_max-index_min)-1;
      int i,count;
      if(steps>=1)
      {
        if(array[index_min]<array[index_max])
        {
          dir=1;
          start = index_min;
          end = index_max;
        }
        else
        {
          dir = -1;
          start = index_max;
          end = index_min;
        }
        for(i=start+dir,count=0;count<steps;i+=dir,count++)
        {
          if(array[i]<=array[start])water += array[start] - array[i];
          else
          {
            if(i<end)water += compute_water(array, i, end);
            else water += compute_water(array, end, i);
            break;
          }
        }
      }
      return water;
    }
    
    int main(int argc,char ** argv)
    {
      int size = 0;
      int * towers;
      if(argc==1)
      {
        cout<< "Usage: "<<argv[0]<< "a list of tower height separated by spaces" <<endl;
      }
      else
      {
        size = argc - 1;
        towers =  (int*)malloc(size*sizeof(int));
        for(int i = 0; i<size;i++)towers[i] = atoi(argv[i+1]);
        cout<< "water collected: "<< compute_water(towers, 0, size-1)<<endl;
        free(towers);
      }
    }
    
    0 讨论(0)
提交回复
热议问题