LeetCode-Go 题解 892:三维形体的表面积(Surface Area of 3D Shapes)—— 网格叠放立方体的表面积求解
【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go
本篇技术指南围绕 LeetCode 892 题「Surface Area of 3D Shapes(三维形体的表面积)」展开,以 LeetCode-Go 仓库中 leetcode/0892.Surface-Area-of-3D-Shapes/README.md 为骨架,结合仓库内 完整 Go 实现 与 单元测试,讲解「先求独立柱体表面积、再扣除相邻重叠面」的经典解法,并给出逐行代码讲解、复杂度分析与可复现的测试验证方式。读完本文,你将掌握这类网格堆叠类几何题的通用解题套路,并能直接运行仓库代码验证结果。
题目描述
在一个N * N的网格上,我们放置一些1 * 1 * 1的立方体。
每个值v = grid[i][j]表示在网格单元格(i, j)上叠放了v个立方体(形成一个竖直柱体)。
请返回最终得到的立体图形的总表面积。
官方给出的五个示例:
| 示例 | 输入 grid | 输出 |
|---|---|---|
| Example 1 | [[2]] | 10 |
| Example 2 | [[1,2],[3,4]] | 34 |
| Example 3 | [[1,0],[0,2]] | 16 |
| Example 4 | [[1,1,1],[1,0,1],[1,1,1]] | 32 |
| Example 5 | [[2,2,2],[2,1,2],[2,2,2]] | 46 |
约束条件(Note):
1 <= N <= 500 <= grid[i][j] <= 50
即网格边长最大为 50,单个柱体最高可叠 50 个立方体,且允许grid[i][j] = 0(该单元格不放置任何立方体)。
题目大意
用一句话概括:给定一个N * N的二维数组,数组中的每个值v = grid[i][j]表示在该单元格上叠放v个1 * 1 * 1的小立方体(竖直堆叠成一个柱体),求这些叠放立方体最终组成的三维形体的外表面面积之和。
需要特别注意的是:本题求的是所有暴露在外的表面,内部被相邻柱体遮挡的面、以及柱体之间相互贴合的接触面都不能计入。
解题思路:独立柱体求和 + 重叠面扣除
核心思想
这是 LeetCode-Go 仓库题解中定位为简单题的一类问题。整体思路非常直观,分两步走:
- 先假设所有柱体彼此独立:每个单元格
(i, j)上叠放v个立方体,形成一个高为v的竖直柱体,其表面积为4 * v + 2(四个侧面共4 * v个面,加上顶部 1 个面、底部 1 个面)。 - 再扣除重叠的面:当相邻的两个柱体彼此接触时,接触部位的面会被遮挡,需要从总面积中减去。每对相邻柱体在接触方向上被遮挡的面数为
2 * min(v1, v2)(两个柱体各有一侧的面被遮住,各为min(v1, v2)个)。
按题目意思,找到叠放时重叠的面,然后用总表面积减去这些重叠的面积,即为最终答案。
重叠面为什么要减两次
这是整个解法的关键细节。假设单元格(i, j)高v1,其右侧邻居(i, j+1)高v2,且v1 < v2:
- 从
(i, j)柱体看,面向右侧的v1个面全部被邻居遮挡; - 从
(i, j+1)柱体看,面向左侧的v1个面也全部被(i, j)柱体遮挡。
所以这一对邻居之间,实际被隐藏的面是2 * min(v1, v2)。在代码实现中,每个单元格只对自己的上、下、左、右四个方向各减一次min(自身高度, 邻居高度),由于每条邻接边会被左右(或上下)两个端点各处理一次,恰好实现了"减两次",与几何事实完全吻合。
边缘与空单元格处理
- 当
grid[i][j] == 0时直接跳过:该位置没有立方体,既没有表面积贡献,也不参与重叠面计算(min(0, x) = 0本身也不会产生扣除,跳过只是省去无意义的计算)。 - 对网格边界处的单元格,只需检查存在的邻居方向(如
i > 0才检查上方,i < len(grid)-1才检查下方),避免数组越界。
Go 实现与逐行讲解
仓库中的完整实现位于 leetcode/0892.Surface-Area-of-3D-Shapes/892. Surface Area of 3D Shapes.go,代码与 README 题解完全一致:
package leetcode func surfaceArea(grid [][]int) int { area := 0 for i := 0; i < len(grid); i++ { for j := 0; j < len(grid[0]); j++ { if grid[i][j] == 0 { continue } area += grid[i][j]*4 + 2 // up if i > 0 { m := min(grid[i][j], grid[i-1][j]) area -= m } // down if i < len(grid)-1 { m := min(grid[i][j], grid[i+1][j]) area -= m } // left if j > 0 { m := min(grid[i][j], grid[i][j-1]) area -= m } // right if j < len(grid[i])-1 { m := min(grid[i][j], grid[i][j+1]) area -= m } } } return area } func min(a, b int) int { if a > b { return b } return a }逐段拆解如下:
- 初始化:
area := 0累积最终表面积。 - 双重遍历:外层
i遍历行、内层j遍历列,覆盖N * N全部单元格。 - 跳过空单元格:
if grid[i][j] == 0 { continue },高度为 0 的单元格不参与计算。 - 累加独立柱体表面积:
area += grid[i][j]*4 + 2。一个高v的柱体有4 * v个侧面(每层立方体贡献 4 个侧面)加上顶部和底部各 1 个面。 - 扣除四个方向的重叠面:
- 上方:
min(grid[i][j], grid[i-1][j]),当i > 0时执行; - 下方:
min(grid[i][j], grid[i+1][j]),当i < len(grid)-1时执行; - 左方:
min(grid[i][j], grid[i][j-1]),当j > 0时执行; - 右方:
min(grid[i][j], grid[i][j+1]),当j < len(grid[i])-1时执行。
- 上方:
- 辅助函数
min:返回两数中的较小者,即两柱体在接触方向上的重叠层数。
用手算验证示例
以 Example 1[[2]]为例:只有一个高度为 2 的柱体,area = 2*4 + 2 = 10,没有邻居可扣除,最终返回10,与预期一致。
以 Example 2[[1,2],[3,4]]为例:
- 四个柱体独立表面积:
6 + 10 + 14 + 18 = 48; - 四条邻接边(横向两条、纵向两条)的重叠量分别为
min(1,2)=1、min(3,4)=3、min(1,3)=1、min(2,4)=2,共扣除2 * (1+3+1+2) = 14; - 最终
48 - 14 = 34,与官方输出一致。
以 Example 3[[1,0],[0,2]]为例:两个柱体独立表面积为6 + 10 = 16,所有邻接边中至少有一端高度为 0,min值均为 0,无需扣除,最终16与预期一致。这也验证了零值单元格不会引入任何额外扣除。
复杂度分析
- 时间复杂度:
O(N²)。算法对N * N个单元格各遍历一次,每个单元格最多进行 4 次常数时间的min比较。 - 空间复杂度:
O(1)。只使用了一个整型变量area累积结果,不随输入规模增长。
在题目约束N <= 50下,最多只有 2500 个单元格,无论时间还是空间都非常充裕。
测试验证:覆盖全部官方示例
仓库为本题提供了完整的单元测试文件 leetcode/0892.Surface-Area-of-3D-Shapes/892. Surface Area of 3D Shapes_test.go。测试采用「参数 + 期望答案」的结构化组织方式:定义para892(输入二维数组one)与ans892(期望输出one),再将二者组合成question892用例列表,最后在Test_Problem892中逐一断言。
测试用例完整覆盖了 README 中的五个官方示例:
| 输入 | 期望输出 |
|---|---|
[[2]] | 10 |
[[1,2],[3,4]] | 34 |
[[1,0],[0,2]] | 16 |
[[1,1,1],[1,0,1],[1,1,1]] | 32 |
[[2,2,2],[2,1,2],[2,2,2]] | 46 |
这五个用例恰好覆盖了各种关键场景:单柱体(Example 1)、满高度矩阵(Example 2、5)、含零值单元格(Example 3)、含凹陷空洞的环状结构(Example 4),能够有效验证重叠面扣除逻辑的正确性。
在仓库根目录执行以下命令即可运行本题测试:
go test -v -run Test_Problem892 "./leetcode/0892.Surface-Area-of-3D-Shapes/"测试运行时会打印每个用例的输入与surfaceArea的计算输出,例如:
【input】:[[2]] 【output】:10 【input】:[[1 2] [3 4]] 【output】:34 【input】:[[1 0] [0 2]] 【output】:16 【input】:[[1 1 1] [1 0 1] [1 1 1]] 【output】:32 【input】:[[2 2 2] [2 1 2] [2 2 2]] 【output】:46若需验证整个仓库的测试与覆盖率,可参考根目录 gotest.sh 中的脚本方式,一次性对全部题解运行带覆盖率统计的测试:
go test -covermode=atomic -coverprofile=coverage.txt ./leetcode/...仓库要求所有题解具备 100% 的测试覆盖率,本题的测试用例正是这一工程规范的体现。仓库使用的 Go 版本为go 1.19(见 go.mod),上述命令可直接在项目根目录下执行。
小结
本题的核心结论可以归纳为一条公式:
总表面积 = Σ(每个非零柱体的 4*v + 2) − 2 * Σ(所有相邻柱体的 min(v1, v2))其中"每个相邻柱体对只计算一次重叠、但扣除时按两侧各一次"是避免重复与遗漏的关键。相比把每个立方体的 6 个面逐一数出来的暴力做法,这种"整体求和、局部扣除"的思路把问题从三维降维到二维邻接关系上,代码简洁且不易出错,也是 LeetCode-Go 仓库对该题给出的标准解法。理解本题后,类似的「网格堆叠 / 相邻贡献扣除」类题目(如岛屿周长、柱状图相关几何题)都可以复用同一套分析框架。
【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考