塔之间收集的水

时间:2014-06-25 17:09:25

标签: algorithm

我最近遇到亚马逊提出的面试问题,我无法找到解决这个问题的优化算法:

您将获得一个输入数组,其中每个元素代表一个线塔的高度。每个塔的宽度是1.它开始下雨。塔之间收集了多少水?

实施例

Input: [1,5,3,7,2] , Output: 2 units
Explanation: 2 units of water collected between towers of height 5 and 7

   *
   *
 *w*
 *w*
 ***
 ****
*****

另一个例子

Input: [5,3,7,2,6,4,5,9,1,2] , Output: 14 units 
Explanation= 2 units of water collected between towers of height 5 and 7 +
             4 units of water collected between towers of height 7 and 6 + 
             1 units of water collected between towers of height 6 and 5 +
             2 units of water collected between towers of height 6 and 9 +
             4 units of water collected between towers of height 7 and 9 +
             1 units of water collected between towers of height 9 and 2.

起初我认为这可以通过库存问题(http://www.geeksforgeeks.org/the-stock-span-problem/)解决,但我错了,所以如果有人能想到这个问题的时间优化算法会很棒。

27 个答案:

答案 0 :(得分:34)

一旦水完成,每个位置将填充到与左侧最高塔和右侧最高塔中较小者相等的水平。

通过向右扫描找到每个位置左侧的最高塔。然后通过向左扫描找到每个位置右侧的最高塔。然后在每个位置取最小值并将它们全部加起来。

这样的事情应该有效:

int tow[N]; // nonnegative tower heights
int hl[N] = {0}, hr[N] = {0}; // highest-left and highest-right
for (int i = 0; i < n; i++) hl[i] = max(tow[i], (i!=0)?hl[i-1]:0);
for (int i = n-1; i >= 0; i--) hr[i] = max(tow[i],i<(n-1) ? hr[i+1]:0);
int ans = 0;
for (int i = 0; i < n; i++) ans += min(hl[i], hr[i]) - tow[i];

答案 1 :(得分:24)

这是Haskell的有效解决方案

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

它使用与其他答案中提到的相同的双程扫描算法。

答案 2 :(得分:7)

请参阅此网站以获取代码,其真正简单明了 http://learningarsenal.info/index.php/2015/08/21/amount-of-rain-water-collected-between-towers/

输入:[5,3,7,2,6,4,5,9,1,2],输出:14个单位

enter image description here 解释

每个塔可以将水保持在最高塔之间的最小高度,最高塔之间的高度。

因此,我们需要计算每个塔的左侧最高塔,同样适用于右侧。

这里我们将需要两个额外的阵列来保持任何塔上的最高塔的高度,例如,int leftMax [],同样右侧说int rightMax []。

STEP-1

我们对给定数组进行左传(即int tower []),并保持临时最大值(比如int tempMax),以便在每个迭代时将每个塔的高度与tempMax进行比较,如果是高度当前塔的温度小于tempMax,则tempMax将设置为其左侧的最高塔,否则当前塔的高度将被指定为左侧最高的塔,tempMax将以当前塔高度更新,

步骤-2

我们将按照步骤1中的讨论,按照上述程序计算最高塔到右边但这次从右侧穿过阵列。

步骤-3

每个塔可以容纳的水量是 -

(最高右塔和最高左塔之间的最小高度) - (塔的高度)

答案 3 :(得分:5)

您可以通过扫描阵列两次来完成此操作。

第一次从上到下扫描并存储到达每行时尚未遇到的最高塔的值。

然后重复此过程,但反过来。你从底部开始,朝着阵列的顶部工作。您可以跟踪到目前为止看到的最高塔,并将其高度与其他结果集中该塔的值进行比较。

取两个值中较小者之间的差异(当前塔周围最高的两个塔中最短的一个,减去塔的高度,并将该量加到总水量中。

int maxValue = 0;
int total = 0;
int[n] lookAhead

for(i=0;i<n;i++)
{
    if(input[i] > maxValue) maxValue = input[i];
    lookahead[i] = maxValue;
}

maxValue = 0;
for(i=n-1;i>=0;i--)
{
    // If the input is greater than or equal to the max, all water escapes.
    if(input[i] >= maxValue)
    { 
        maxValue = input[i];
    }
    else
    {
        if(maxValue > lookAhead[i])
        {
            // Make sure we don't run off the other side.
            if(lookAhead[i] > input[i])
            {
                total += lookAhead[i] - input[i];
            }
        }
        else
        {
            total += maxValue - input[i];
        }
    } 
}

答案 4 :(得分:4)

Java中的O(n)解决方案,单次传递

Java中的另一个实现,通过列表单次查找收集的水。我扫描了其他答案,但没有看到任何明显使用我的解决方案。

  1. 找到第一个&#34;峰值&#34;通过循环列表直到塔高停止增加。之前的所有水都不会被收集(排到左边)。
  2. 对于所有后续塔楼:
    • 如果后续塔的高度降低或保持不变,请将水加入潜在的集合中。铲斗,等于塔架高度与之前最大塔架高度之间的差值。
    • 如果后续塔的高度增加,我们从前一个桶中收集水(从&#34;潜在的集合&#34;桶中减去并添加到收集的桶中)并且还将水添加到等于的潜在桶中塔架高度与先前最大塔架高度之间的差异。
    • 如果我们找到一个新的最大塔,那么所有潜在的水&#34;被移入收集的桶中,这将成为新的最大塔高。
  3. 在上面的示例中,使用输入:[5,3,7,2,6,4,5,9,1,2],解决方案的工作原理如下:

      
        
    • 5:找到5作为第一个峰值
    •   
    • 3:向潜在的桶添加2(5-3)

      收集= 0,潜力= 2

    •   
    • 7:新的最大值,将所有潜在的水移动到收集的桶中

      收集= 2,潜力= 0

    •   
    • 2:向潜在的桶添加5(7-2)

      收集= 2,潜力= 5

    •   
    • 6:将4移动到收集的桶中并向潜在桶添加1(6-2,7-6)

      收集= 6,潜力= 2

    •   
    • 4:向潜在的桶添加2(6-4)

      收集= 6,潜力= 4

    •   
    • 5:将1移动到收集的桶中,并将2添加到潜在桶(5-4,7-5)

      收集= 7,潜力= 6

    •   
    • 9:新的最大值,将所有潜在的水移动到收集的桶

      收集= 13,潜力= 0

    •   
    • 1:向潜在的桶添加8(9-1)

      收集= 13,潜力= 8

    •   
    • 2:将1移动到收集的桶中,并将7添加到潜在桶(2-1,9-2)

      收集= 14,潜力= 15

    •   

    经过一次清单后,测量了收集的水量。

    public static int answer(int[] list) {
        int maxHeight = 0;
        int previousHeight = 0;
        int previousHeightIndex = 0;
        int coll = 0;
        int temp = 0;
    
        // find the first peak (all water before will not be collected)
        while(list[previousHeightIndex] > maxHeight) {
            maxHeight = list[previousHeightIndex];
            previousHeightIndex++;
            if(previousHeightIndex==list.length)            // in case of stairs (no water collected)
                return coll;
            else
                previousHeight = list[previousHeightIndex];
        }
    
        for(int i = previousHeightIndex; i<list.length; i++) {
            if(list[i] >= maxHeight) {      // collect all temp water
                coll += temp;
                temp = 0;
                maxHeight = list[i];        // new max height
            }
            else {
                temp += maxHeight - list[i];
                if(list[i] > previousHeight) {  // we went up... collect some water
                    int collWater = (i-previousHeightIndex)*(list[i]-previousHeight);
                    coll += collWater;
                    temp -= collWater;
                }
            }
    
            // previousHeight only changes if consecutive towers are not same height
            if(list[i] != previousHeight) {
                previousHeight = list[i];
                previousHeightIndex = i;
            }
        }
        return coll;
    }
    

答案 5 :(得分:4)

可读的Python解决方案:

def water_collected(heights):
    water_collected = 0
    left_height = []
    right_height = []

    temp_max = heights[0]
    for height in heights:
        if (height > temp_max):
            temp_max = height
        left_height.append(temp_max)

    temp_max = heights[-1]
    for height in reversed(heights):
        if (height > temp_max):
            temp_max = height
        right_height.insert(0, temp_max)

    for i, height in enumerate(heights):
        water_collected += min(left_height[i], right_height[i]) - height
    return water_collected

答案 6 :(得分:3)

已发布的17个答案中没有一个真的是时间最优的。

对于单个处理器,2扫描(left->right,后跟right->left求和)是最佳的,正如许多人所指出的那样,但是使用了许多处理器,可以在O(log n)时间内完成此任务。有很多方法可以做到这一点,所以我将解释一个与顺序算法非常接近的方法。

最大缓存树O(log n)

1:创建所有塔的二叉树,使每个节点包含其任何子塔中最高塔的高度。由于任何节点的两个叶子都可以独立计算,因此可以在O(log n)时间内使用n cpu来完成。

2a:然后,对于树中的每个节点,从根开始,让右边的叶子具有值max(left, self, right)。这将使用O(log n) cpu&#39}在n时间内创建从左到右的单调扫描。

2b:为了计算从右向左扫描,我们执行与以前相同的过程。从max-cached树的根开始,让左叶具有值max(left, self, right)。如果您愿意,这些从左到右(2a)和从右到左(2b)的扫描可以并行完成。它们都使用max-cached树作为输入,并且每个树都生成一个新树(或者如果您愿意,可以在原始树中设置自己的字段)。

3:然后,对于每个塔,其上的水量为min(ltr, rtl) - towerHeight,其中ltr是我们之前从左到右单调扫描中该塔的值,即我们左边任何塔的最大高度(包括我们自己 1 ),rtl对于从右到左的扫描是相同的。

4:只需使用O(log n) cpu&#39}在n时间使用树进行总结,我们就完成了。

1 如果当前的塔比我们左边的所有塔高,或者比我们右边的所有塔都高,min(ltr, rtl) - towerHeight为零。

此处two other ways to do it

答案 7 :(得分:2)

以下是两次传递中Groovy的解决方案。

assert waterCollected([1, 5, 3, 7, 2]) == 2
assert waterCollected([5, 3, 7, 2, 6, 4, 5, 9, 1, 2]) == 14
assert waterCollected([5, 5, 5, 5]) == 0
assert waterCollected([5, 6, 7, 8]) == 0
assert waterCollected([8, 7, 7, 6]) == 0
assert waterCollected([6, 7, 10, 7, 6]) == 0

def waterCollected(towers) {
    int size = towers.size()
    if (size < 3) return 0

    int left = towers[0]
    int right = towers[towers.size() - 1]

    def highestToTheLeft = []
    def highestToTheRight = [null] * size

    for (int i = 1; i < size; i++) {

        // Track highest tower to the left
        if (towers[i] < left) {
            highestToTheLeft[i] = left
        } else {
            left = towers[i]
        }

        // Track highest tower to the right
        if (towers[size - 1 - i] < right) {
            highestToTheRight[size - 1 - i] = right
        } else {
            right = towers[size - 1 - i]
        }
    }

    int water = 0
    for (int i = 0; i < size; i++) {
        if (highestToTheLeft[i] && highestToTheRight[i]) {
            int minHighest = highestToTheLeft[i] < highestToTheRight[i] ? highestToTheLeft[i] : highestToTheRight[i]
            water += minHighest - towers[i]
        }
    }

    return water
}

这里有一个在线编译器的相同代码段: https://groovy-playground.appspot.com/#?load=3b1d964bfd66dc623c89

答案 8 :(得分:2)

您可以先从左向右移动,并计算在左侧较小的建筑物和右侧较大的建筑物的情况下累积的水量。您必须减去这两栋建筑物之间的建筑物面积,并且小于左侧建筑物的面积。

从右到左也是如此。

这是从左到右的代码。我已经使用这种方法在leetcode在线评判上传了这个问题。

我发现这种方法比任何地方都存在的标准解决方案更直观(计算每个i的右边和左边最大的建筑物)。

int sum=0, finalAns=0;
    idx=0;
    while(a[idx]==0 && idx < n)
        idx++;
    for(int i=idx+1;i<n;i++){

        while(a[i] < a[idx] && i<n){
            sum += a[i];
            i++;
        }
        if(i==n)
            break;
        jdx=i;
        int area = a[idx] * (jdx-idx-1);
        area -= sum;
        finalAns += area;

        idx=jdx;
        sum=0;
    }

这种方法的时间复杂度是O(n),因为你是两次线性遍历数组。 空间复杂度为O(1)。

答案 9 :(得分:2)

列表中的第一个和最后一个条不能捕获水。对于其余的塔,当左侧和右侧有最大高度时,它们可以捕获水。

积水是:   max(min(max_left,max_right) - current_height,0)

从左侧迭代,如果我们知道max_right更大,则min(max_left,max_right)将变为max_left。因此,水的积累简化为: max(max_left - current_height,0)从右侧考虑相同的模式。

从上面的信息中,我们可以编写一个O(N)时间和O(1)空间算法如下(在Python中):

def trap_water(A):

     water = 0
     left, right = 1, len(A)-1
     max_left, max_right = A[0], A[len(A)-1]

     while left <= right:
         if A[left] <= A[right]:
             max_left = max(A[left], max_left)
             water += max(max_left - A[left], 0)
             left += 1
         else:
             max_right = max(A[right], max_right)
             water += max(max_right - A[right], 0)
             right -= 1

     return water

答案 10 :(得分:1)

我是根据此线程中上面的一些想法编写的:

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)

好吧...

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

> 5

答案 11 :(得分:1)

我丑陋的单遍历溶解

def water_collection(buildings):
  valleyFlag = False
  water = 0
  pool = []
  for i, building in enumerate(buildings):
    if(i == 0):
      lastHill = building
    else:
      if lastHill <= building or i == len(buildings)-1:
        minHill = min(building, lastHill)
        print("Hill {} to Hill {}".format(lastHill, building))
        summ = 0
        for drop in pool:
          summ += minHill - drop
          water += minHill - drop
        print("Collected sum {}".format(summ))
        pool = []
        valleyFlag = False
        lastHill = building
      elif lastHill > building and valleyFlag == False:
        pool.append(building)
        valleyFlag = True
      elif lastHill > building and valleyFlag == True:
        pool.append(building)

  print(water)

答案 12 :(得分:1)

这是在Scala上编写的另一个解决方案

def find(a: Array[Int]): Int = {
  var count, left, right = 0

  while (left < a.length - 1) {
    right = a.length - 1
    for (j <- a.length - 1 until left by -1) {
      if (a(j) > a(right)) right = j
    }

    if (right - left > 1) {
      for (k <- left + 1 until right) count += math.min(a(left), a(right)) - a(k)
      left = right
    } else left += 1
  }

  count
}

答案 13 :(得分:1)

欧几里德风格的替代算法,我认为比所有扫描更优雅:

  

将两座最高的塔楼设为左右塔楼。大量的   这些塔之间的水很明显。

     

选择下一个最高的塔并添加它。它必须在...之间   终端塔,或不。如果它在终端塔之间它取代了   水量等于塔的体积(感谢阿基米德的   这个提示)。如果它在终端塔外面,它将成为一个新的终端塔   含水量明显增加。

     

重复下一个最高的塔,直到所有塔都被添加。

我已经发布了代码来实现这一目标(在现代的欧几里德成语中):http://www.rosettacode.org/wiki/Water_collected_between_towers#F.23

答案 14 :(得分:0)

/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
    let maxLeftArray = [], maxRightArray = [];
    let maxLeft = 0, maxRight = 0;
    const ln = height.length;
    let trappedWater = 0;
    for(let i = 0;i < height.length; i ++) {
        maxLeftArray[i] = Math.max(height[i], maxLeft);
        maxLeft = maxLeftArray[i];
        maxRightArray[ln - i - 1] = Math.max(height[ln - i - 1], maxRight);
        maxRight = maxRightArray[ln - i - 1];
    }
    for(let i = 0;i < height.length; i ++) {
        trappedWater += Math.min(maxLeftArray[i], maxRightArray[i]) - height[i];
    }
    return trappedWater;
};

var arr = [5,3,7,2,6,4,5,9,1,2];
console.log(trap(arr));

您可以阅读我的博客文章中的详细说明:trapping-rain-water

答案 15 :(得分:0)

这是一个有趣的问题,我在接受采访时得到了这个问题。大声笑我打破了这个愚蠢的问题,找到了一个需要一次通过的解决方案(但显然是不连续的)。 (事实上​​,当你绕过边界时,你甚至不会遍历整个数据......)

所以这个想法是。你从最低塔(现在是参考)的一侧开始。您直接添加塔的内容,如果到达高于参考的塔,则递归调用该函数(将侧重置)。代码用文字解释并非易事,代码不言而喻。

  #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);
  }
}

答案 16 :(得分:0)

测试了所提供的所有Java解决方案,但是它们都没有通过我提出的一半测试用例,所以还有一个 Java O(n)解决方案,所有可能的案例。算法非常简单:

1)从头开始遍历输入,搜索与给定塔相等或更高的塔,同时将下塔的可能水量总计为临时变量。

2)塔找到后 - 将该临时变量添加到主结果变量中并缩短输入列表。

3)如果找不到塔,则反转剩余的输入并再次计算。

public int calculate(List<Integer> input) {
    int result = doCalculation(input);
    Collections.reverse(input);
    result += doCalculation(input);
    return result;
}

private static int doCalculation(List<Integer> input) {
    List<Integer> copy = new ArrayList<>(input);
    int result = 0;
    for (ListIterator<Integer> iterator = input.listIterator(); iterator.hasNext(); ) {
        final int firstHill = iterator.next();
        int tempResult = 0;
        int lowerHillsSize = 0;
        while (iterator.hasNext()) {
            final int nextHill = iterator.next();
            if (nextHill >= firstHill) {
                iterator.previous();
                result += tempResult;
                copy = copy.subList(lowerHillsSize + 1, copy.size());
                break;
            } else {
                tempResult += firstHill - nextHill;
                lowerHillsSize++;
            }
        }
    }
    input.clear();
    input.addAll(copy);
    return result;
}

对于测试用例,请查看此test class

如果发现未发现的测试用例,请随意创建拉取请求。

答案 17 :(得分:0)

以下是我对这个问题的看法, 我使用一个循环来查看之前的塔是否比实际塔大。 如果是,那么我创建另一个循环来检查在实际的塔之后的塔是否大于或等于前一个塔。 如果是这种情况,那么我只是添加前一个塔和所有其他塔之间的所有高度差异。 如果没有,如果我的循环到达我的最后一个对象,那么我只需反转数组,以便前一个塔成为我的最后一个塔,并在其上递归调用我的方法。 这样我肯定会找到比我之前新建的塔更大的塔,并且会找到正确的水量。

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}));
    }
}

答案 18 :(得分:0)

这是我的解决方案,它通过这个级别,非常快速,易于理解 这个想法非常简单:首先,你弄清楚高度的最大值(它可能是多个最大值),然后你将景观切割成3个部分,从最开始到最左边的最大高度,最左边的最大高度到最右边,从最右边到最后。

在中间部分,很容易收集降雨,一个用于循环。然后,对于第一部分,您将继续更新当前最大高度,该高度小于景观的最大高度。一个循环就是这样。然后,对于第三部分,您将对第一部分所做的内容反转

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)        

答案 19 :(得分:0)

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;
    }

答案 20 :(得分:0)

用于查找商店总水量的JavaScript程序:

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);

答案 21 :(得分:0)

这是JAVA中的一个解决方案,它遍历数字列表一次。所以最坏的情况是O(n)。 (至少我是如何理解的)。

对于给定的参考编号,请继续查找大于或等于参考编号的编号。保留执行此操作所经过的数字计数并将所有这些数字存储在列表中。

这个想法是这样的。如果6到9之间有5个数字,并且所有5个数字都是0,则意味着在6到9之间可以存储总共30个单位的水。对于真实情况,其间的数字不是&如果这些数字为0,我们只是从总金额中扣除数字之间的总和。(在这种情况下,我们从30中扣除)。这将计算存储在这两座塔之间的水量。然后我们将这个数量保存在一个名为totalWaterRetained的变量中,然后在9之后从下一个塔开始并继续保持相同直到最后一个元素。

添加totalWaterRetained的所有实例将为我们提供最终答案。

JAVA解决方案:(测试了一些输入。可能不是100%正确)

private static int solveLineTowerProblem(int[] inputArray) {
    int totalWaterContained = 0;
    int index;
    int currentIndex = 0;
    int countInBetween = 0;
    List<Integer> integerList = new ArrayList<Integer>();

    if (inputArray.length < 3) {
        return totalWaterContained;
    } else {
        for (index = 1; index < inputArray.length - 1;) {
            countInBetween = 0;
            integerList.clear();

            int tempIndex = index;
            boolean flag = false;

            while (inputArray[currentIndex] > inputArray[tempIndex] && tempIndex < inputArray.length - 1) {
                integerList.add(inputArray[tempIndex]);
                tempIndex++;
                countInBetween++;
                flag = true;
            }

            if (flag) {
                integerList.add(inputArray[index + countInBetween]);
                integerList.add(inputArray[index - 1]);

                int differnceBetweenHighest = min(integerList.get(integerList.size() - 2),
                        integerList.get(integerList.size() - 1));
                int totalCapacity = differnceBetweenHighest * countInBetween;
                totalWaterContained += totalCapacity - sum(integerList);
            }
            index += countInBetween + 1;
            currentIndex = index - 1;
        }
    }
    return totalWaterContained;
}

答案 22 :(得分:0)

针对此问题的直观解决方案是根据左右边界的高度限制问题并填充水。

我的解决方案:

  • 从左侧开始,将两个边界设置为第0个索引。
  • 检查并查看是否存在某种轨迹(如果您是 走在这些塔顶上,你会不会再往下走,然后再回来 再次?)如果是这样,那么你找到了一个右边界。
  • 现在回溯并相应填充水(我只是添加了 水到数组值本身,因为它使代码有点 更清洁,但这显然不是必需的。)
  • 打孔线:如果左边界塔高度大于 右边界塔高度比你需要增加右边 界。原因是因为你可能遇到更高的塔,需要补充更多的水。 但是,如果右塔比左塔高,那么没有 在您当前的子问题中可以添加更多的水。因此,你移动 你的左边绑定到右边界并继续。

以下是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++;

        }

我毫不怀疑有更多的最佳解决方案。我目前正在进行单程优化。这个问题还有一个非常巧妙的堆栈实现,它使用了与 bounding 相似的概念。

答案 23 :(得分:0)

这是我在Python中的用法。很确定它有效但没有测试过。

两次通过列表(但在找到'water'时删除列表):

def answer(高度):

def accWater(lst,sumwater=0):
    x,takewater = 1,[]
    while x < len(lst):
        a,b = lst[x-1],lst[x]
        if takewater:
            if b < takewater[0]:
                takewater.append(b)
                x += 1
            else:
                sumwater += sum(takewater[0]- z for z in takewater)
                del lst[:x]
                x = 1
                takewater = []
        else:
            if b < a:
                takewater.extend([a,b])
                x += 1
            else:
                x += 1
    return [lst,sumwater]

heights, swater = accWater(heights)
x, allwater = accWater(heights[::-1],sumwater=swater)
return allwater

答案 24 :(得分:0)

这是我在jQuery中的尝试。它只扫描到右边。

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);

答案 25 :(得分:0)

我有一个只需要从左到右进行一次遍历的解决方案。

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

答案 26 :(得分:-1)

# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
#    http://shiny.rstudio.com/
#

library(shiny)
library(ggplot2)
library(plotly)

# Define UI for application that draws a histogram
ui <- fluidPage(
    
    # Application title
    titlePanel("Squished Graph Reproducible Example"),
    
    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        # Show a plot of the generated distribution
        sidebarPanel(),
        
        mainPanel(
            tabsetPanel(
                tabPanel('Tab1', plotlyOutput('plot1')),
                tabPanel('Tab2', plotlyOutput('plot2')),
                tabPanel('Tab3', plotlyOutput('plot3'))
            )
        )
    )
)

# Define server logic required to draw a histogram
server <- function(input, output) {
    
    output$plot1 <- renderPlotly({
        Sys.sleep(1) # represents time for other calculations
        p <- ggplot(mtcars, aes(x=wt, y=drat, color=cyl)) +
            geom_line() +
            theme(legend.position = 'none')
        
        ggplotly(p)
    })
    
    output$plot2 <- renderPlotly({
        Sys.sleep(1) # represents time for other calculations
        p <- ggplot(mtcars, aes(x=disp, y=drat, color=cyl)) +
            geom_line() +
            theme(legend.position = 'none')
        
        ggplotly(p)
    })
    
    output$plot3 <- renderPlotly({
        Sys.sleep(1) # represents time for other calculations
        p <- ggplot(mtcars, aes(x=qsec, y=drat, color=cyl)) +
            geom_line() +
            theme(legend.position = 'none')
        
        ggplotly(p)
    })
}

# Run the application 
shinyApp(ui = ui, server = server)