对攒青豆这一题的Go语言题解

84 阅读2分钟

当青训营遇上码上掘金

这是一篇姗姗来迟的题解,当其他同学都在用C/C++解题的时候我还在努力学Go,就为了这一天能用Go解出此题,虽然语言不重要,但至少证明了努力没辜负!

首先请允许我附上题目描述:

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

image.png

输入样例就不给了,思路到了就行。

首先这是一道很应景的题,我第一眼看到这道题的时候想到的是双循环、双指针,或者分治,当然我个人是没有学习过正统的算法,因此只能基于个人思考解题,显然并非最优解。

在我深思熟虑下不了手的时候,我猛然发现,虽然现实中豆子和墙的长宽、体积等数据无法比较,但是在程序中的单位一定是可以统一的,于是不妨将墙体横着看,分为一层一层的“新墙”,于是从底层开始分析,显然豆子的数量由每层第一个和最后一个最高的墙体决定,在分层后,这个高度就不用计数了,只有相等和更高两个概念,然后我们只需要找出最高的第一个和最后一个墙体,再依次增加层数不断动态更新青豆数量即可。

这么看来时间复杂度显然是O(n2),不能说比双指针等方式更高效,但是也好过多重循环暴力解题,此外go语言中的指针不能进行地址计算也是一个很大的制约。

那么再次附上个人代码,代码中已经注明必要的注释,这个方法曾经在某系地方见到过,可能会与部分同学雷同,此情况实属巧合,希望互相借鉴学习!

package main

import (
	"fmt"
)

func maxL(height []int) int {
	maxL := height[0]
	for i := 1; i < len(height); i++ {
		if height[i] > maxL {
			maxL = height[i]
		}
	}
	return maxL
}

func solution(height []int) int {
	// 用于存储计算结果
	num := 0
	// 数组长度
	sliceLen := len(height)
	// begin  当前层第一个高出来的墙
        // end    当前层最后一个高出来的墙
	var begin, end = 0, sliceLen - 1
	wallHeight := maxL(height) // 最高墙
	//从第一层开始遍历
	for i := 1; i <= wallHeight; i++ {
		// 求当前层的begin和end
		for begin <= end {
			if height[begin] >= i {
				break
			}
			begin++
		}
		for begin <= end {
			if height[end] >= i {
				break
			}
			end--
		}
		// 在当前层寻找可以容纳青豆的空隙
		for j := begin + 1; j < end; j++ {
			if height[j] < i {
				num++
			}
		}
	}
	return num
}

func main() {
	//切片长度
	sliceLen := 0
	_, err := fmt.Scanf("%d", &sliceLen)
	if err != nil {
		fmt.Println("No scanf")
		return
	}
	//切片
	height := make([]int, sliceLen)
	for i := 0; i < sliceLen; i++ {
		_, err := fmt.Scanf("%d", &height[i])
		if err != nil {
			fmt.Println("No scanf")
			return
		}
	}
	fmt.Println(solution(height))
}

那么简单说明一下,solution函数则是题解,maxL函数仅用于找出最高的墙体并在solution中唯一被调用,solution作为核心函数也被main函数唯一调用,时间复杂度在双重循环部分被拉高,目前没有更好的办法。

很高兴能用go语言解出这道题,学以致用何尝不是一种成就!