当青训营遇上码上掘金

41 阅读2分钟

题目:攒青豆

现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)

以下为上图例子的解析:

输入:height = [5,0,2,1,4,0,1,0,3]

输出:17

解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。

解题思路

柱子呈锯齿状,情况多变,我们先从某些简单的局部切入进行分析:

首先分析样例中的前三个柱子:[5,0,2],显然,他们可以囤积青豆的区域为下图中的红框中的区域:

同样的,我们再来分析第2、第3、第4根柱子[2,1,4],现在,他们可以囤积青豆的区域为下图中的黄框中的区域:

那么把前五根柱子放在一起来看,会发现又多了一个区域可以囤积青豆,就是下图中的橙色区域:

可以知道,这片区域是由[5,2,4]这三根柱子决定的。

即:[5,0,2]决定红色区域,[2,1,4]决定黄色区域,[5,2,4]决定橙色区域。

类似的,对于样例中的右边,也可以得到类似的结论:

[4,0,1]决定1号区域,[1,0,3]决定2号区域,[4,1,3]决定3号区域。

规律总结:

每根柱子,以及左右两边第一根比其高的柱子,这样的三根柱子围成的区域的面积总和,就是可以囤积青豆的总数。

代码实现

遍历

使用遍历的方法寻找左右两边第一根比其高的柱子并记录下来,最后计算区域总和。

func gather1(heights []int)(int){
  length := len(heights)
  left := make([]int, length, length);
  right := make([]int, length, length);
  for i, h := range(heights){
    left[i] = i;
    for j := i-1; j >=0; j--{
      if ( heights[j] > h ){
        left[i] = j;
        break;
      }
    }
    right[i] = i;
    for j := i+1; j<length; j++{
      if ( heights[j] >= h ){
        right[i] = j;
        break;
      }
    }
  }
  ans := 0
  for i, h := range(heights){
    height := min( heights[ left[i] ], heights[ right[i] ] ) - h
    ans += height*(right[i]-left[i] -1)
  }
  return ans
}

时间复杂度O(n^2), 空间复杂度O(n)

单调栈

对于寻找左右两边第一个比自己大(或小)的问题,可以使用单调栈来解决,达到更好的时间性能。

func gather2(heights []int)(int){
  length := len(heights)
  left := make([]int, length, length);
  right := make([]int, length, length);
  stack := make([]int, length, length);
  top := 0
  for i, h := range(heights){
    for top > 0 && heights[ stack[top-1] ] <= h {
      right[ stack[top-1] ] = i;
      // 出栈
      top --;
    }
    if top == 0{
      left[i] = i;
    } else{
      left[i] = stack[top-1];
    }
    // 入栈
    stack[top] = i;
    top++;
  }
  for top > 0 {
    right[ stack[top-1] ] = stack[top-1]
    // 出栈
    top--;
  }
  ans := 0
  for i, h := range(heights){
    height := min( heights[ left[i] ], heights[ right[i] ] ) - h
    ans += height*(right[i]-left[i] -1)
  }
  return ans
}

时间复杂度O(n), 空间复杂度O(n)

运行效果