攒青豆问题的两种解法 | 青训营 X 码上掘金

120 阅读2分钟

当青训营遇上码上掘金,主题4的攒青豆问题的两种解法

题目描述

「青训营 X 码上掘金」主题创作活动入营版 开启! - 掘金 (juejin.cn)

解法1 按层数豆子

主要思想:如下图所示:按每行来数豆子的个数(如果柱子高度为0,则可以存放一个单位的豆子),我们每数完一层就让柱子的高度减一。 image.png 第一层:柱子高度为 [5, 0, 2, 1, 4, 0 ,1, 0, 3],可以攒的豆子数为3

第二层:柱子高度为 [4, 0, 1, 0, 3, 0, 0, 0, 2],可以攒的豆子数为5

第三层:柱子高度为 [3, 0, 0, 0, 2, 0, 0, 0, 1],可以攒的豆子数为6

第四层:柱子高度为 [2, 0, 0, 0, 1, 0, 0, 0, 0],可以攒的豆子数为3

第五层:柱子高度为 [1, 0, 0, 0, 0, 0, 0, 0, 0],可以攒的豆子数为0

因此一共可以攒17个单位的豆子,Go语言代码如下:

func getBeansByLine(height []int) int {
   ans := 0
   for {
      flag := false
      tmp := 0
      for i, h := range height {
         if !flag && h != 0 {
            flag = true
            height[i]--
            continue
         }
         if flag && h == 0 {
            tmp++
            continue
         }
         if h != 0 {
            ans += tmp
            tmp = 0
            height[i]--
         }
      }
      if !flag {
         break
      }
   }
   return ans
}

假设柱子数为n,最大高度为m则:时间复杂度为O(n×m),空间复杂度为O(1)

解法2 单调栈

主要思想:用一个单调栈来存储柱子的位置信息,当前柱子高度小于等于栈顶柱子高度时入栈,当前柱子高度大于栈顶柱子高度时出栈,并且计算两根柱子之间可以收集的青豆数,重复直到当前柱子的高度小于等于栈顶柱子高度或者栈空为止。

func getBeansUsingStack(height []int) int {
   ans := 0
   var stack []int // 用切片来表示一个单调栈
   for i, h := range height {
      for len(stack) > 0 && h > height[stack[len(stack)-1]] {
         // 栈不为空且当前柱子高度大于栈顶柱子高度
         top := stack[len(stack)-1]   // 获取栈顶柱子位置
         stack = stack[:len(stack)-1] // 栈顶元素出栈
         if len(stack) == 0 {         // 长度为0时表示栈为空
            break
         }
         left := stack[len(stack)-1]
         distance := i - left - 1                        // 柱子之间的距离
         minHeight := min(height[left], h) - height[top] // 两根柱子中较短的一根
         ans += distance * minHeight
      }
      stack = append(stack, i)
   }
   return ans
}

该方法的时间复杂度为O(n),空间复杂度为O(n)