接青豆码上掘金题目

52 阅读2分钟

当青训营遇上码上掘金

今天做了码上掘金的一道题目,题目描述如下。

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

725ef710a59944d49d0315bece7a1ac1_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.webp

这道题目有很多种解法,我们首先先说一下双指针的方法,我们可以从每一列开始计算,默认下标宽度为1,求出每一列的高度,然后求和就可以得知我们可以得到的青豆的数量。每一列的高度取决于它左右相邻两列的高度。如图中所示,第二列左侧的最高列是5,右侧的最高列是4,所以第二列能够存储的青豆应该是左右最高列中较小的一个,也就是4,减去第二列本身的高度0,所以第二列能够存储的青豆数量是4。后面的每一列都是同理。把每一列存储青豆的数量加起来,得到最后的结果。

具体代码如下:

func countQingDou(height []int) int {
   //定义左右两个指针
   left, right := 0, len(height)-1
   //定义两个最大值
   leftMax, rightMax := 0, 0
   //最后结果
   var res int
   for left < right {
      leftMax = max(leftMax, height[left])
      rightMax = max(rightMax, height[right])
      if height[left] < height[right] {
         res += leftMax - height[left]
         left++
      } else {
         res += rightMax - height[right]
         right--
      }
   }
   return res
}
func max(a, b int) int {
   if a > b {
      return a
   } else {
      return b
   }
}
func main() {
   //_, err := GetSnapshot("./public/bear.mp4", "./public/bear", 1)
   //if err != nil {
   // return
   //}
   //fmt.Println(time.Now())

   // 接收数据输入
   sc := bufio.NewScanner(os.Stdin)
   sc.Scan()
   strs := strings.Split(sc.Text(), " ")
   nums := make([]int, 0)
   for i := range strs {
      n, _ := strconv.Atoi(strs[i])
      nums = append(nums, n)
   }
   res := countQingDou(nums)
   fmt.Println(res)
}

这么做的时间复杂度是O(n2),空间复杂度是O(1)

这道题目也可以使用动态规划来做,基本原理和上面是一样的,都是求左右两边最高柱子中小的那个。 向左边找的递推公式是:maxLeft[j]=max(high[i],maxLeft[i-1]) 从右向左遍历:maxRight[i] = max(high[i],maxRight[i+1]) 记录下来之后,求和,得到最后的结果。

这个题目还可以使用单调栈来做,但是是比较复杂的,这里仅仅提供一个思路,大家有兴趣可以自己尝试一下。