LeetCode-Go 题解 566:Reshape the Matrix 矩阵重塑(MATLAB reshape 原理与 Go 实现剖析)
本文以 LeetCode-Go 仓库中 0566.Reshape-the-Matrix 题解目录 为核心,完整讲解 LeetCode 第 566 题「重塑矩阵」的题目背景、判定条件与模拟填充思路,并逐行剖析该仓库的 Go 实现与测试用例。读完本文,你将掌握"元素总数守恒 + 行遍历填充"这一通用矩阵重塑范式,并能在不借助额外一维数组的情况下完成原地重排。
题目描述:MATLAB reshape 的二维数组版本
在 MATLAB 中,reshape 是一个非常有用的函数,它可以把一个矩阵重塑为大小不同但保留原始数据的新矩阵。LeetCode 566 题要求用二维数组实现同样的能力:
给出一个由二维数组表示的矩阵,以及两个正整数
r和c,分别表示想要重构出的矩阵的行数和列数。重构后的矩阵需要将原始矩阵的所有元素**以相同的行遍历顺序(row-traversing order)**填充。如果该 reshape 操作可行且合法,则输出新的重塑矩阵;否则,输出原始矩阵。
通俗地说:把原矩阵按"先从左到右、再从上到下"的顺序读出所有元素,再按同样的顺序逐行填入新矩阵。关键在于,只有元素总数相等时重塑才合法。
示例 1:展平成单行
Input:
nums =
[[1,2],
[3,4]]
r = 1, c = 4
Output:
[[1,2,3,4]]
原矩阵的行遍历序列为 [1,2,3,4],将其逐行填入 1 × 4 的新矩阵即得到 [[1,2,3,4]]。
示例 2:元素总数不匹配,返回原矩阵
Input:
nums =
[[1,2],
[3,4]]
r = 2, c = 4
Output:
[[1,2],
[3,4]]
2 × 2 = 4 个元素无法装进 2 × 4 = 8 个格子,重塑不合法,因此输出原矩阵。
数据范围说明
- 给定矩阵的高和宽均在
[1, 100]范围内; - 给定的
r和c均为正整数。
这意味着输入矩阵永不为空,nums[0] 始终存在,后续代码可以直接取 len(nums[0]) 而无需判空,这是实现细节中的一个隐含前提。
核心解题思路:元素总数守恒 + 行遍历填充
这是一道典型的模拟(simulation)题,思路分两步:
- 合法性判定:设原矩阵为
m × n,目标矩阵为r × c。只有当m × n == r × c时重塑才可行; - 顺序填充:按行遍历原矩阵的每个元素,以相同的先后顺序逐行填入新矩阵。
只要抓住"原矩阵的行遍历顺序 = 新矩阵的填充顺序"这一等量关系,无论目标矩阵的形状如何变化,元素的相对顺序都不会被打乱。这也是 MATLAB reshape 在底层所遵循的基本语义。
Go 实现解析:仓库源码逐行剖析
LeetCode-Go 仓库在 566. Reshape the Matrix.go 中给出了完整实现,整体拆分为三个职责单一的函数:
func matrixReshape(nums [][]int, r int, c int) [][]int {
if canReshape(nums, r, c) {
return reshape(nums, r, c)
}
return nums
}
matrixReshape 是入口函数:先调用 canReshape 做合法性判定,可行则调用 reshape 执行重塑,否则原样返回 nums。这种"判定与执行分离"的写法让每个函数只做一件事,逻辑清晰、易于测试。
合法性判定:canReshape
func canReshape(nums [][]int, r, c int) bool {
row := len(nums)
colume := len(nums[0])
if row*colume == r*c {
return true
}
return false
}
判定条件就是元素总数守恒:len(nums) 得到原矩阵行数 m,len(nums[0]) 得到原矩阵列数 n,只有当 m*n == r*c 时返回 true。由于题目保证矩阵高宽在 [1, 100] 内、r、c 均为正整数,这里不需要处理空矩阵或零尺寸的边界情况。
重塑填充:reshape
func reshape(nums [][]int, r, c int) [][]int {
newShape := make([][]int, r)
for index := range newShape {
newShape[index] = make([]int, c)
}
rowIndex, colIndex := 0, 0
for _, row := range nums {
for _, col := range row {
if colIndex == c {
colIndex = 0
rowIndex++
}
newShape[rowIndex][colIndex] = col
colIndex++
}
}
return newShape
}
填充阶段有两个关键点:
- 先分配再填充:先
make出r行,再为每一行make出长度为c的切片,得到完整的r × c零值矩阵; - 用游标模拟行遍历:维护
rowIndex与colIndex两个游标。每次写入一个元素后colIndex++;当colIndex == c时说明当前行已写满,将colIndex归零、rowIndex++换到下一行。
由于 canReshape 已保证元素总数相等,双游标恰好会在填完最后一个元素时到达 (r, c) 的末尾,不存在越界或剩余元素的情况。这种写法无需借助额外的一维数组做中转,直接在目标矩阵上按位置落值,空间上更省。
注意:仓库源码中使用的变量名
colume是column的笔误,属于无伤大雅的命名瑕疵,不影响功能正确性。读者在自行实现时建议使用规范拼写column。
另一种等价实现:先展平后按行切片
除仓库的双游标方案外,另一种常见写法是先读取原矩阵的行遍历序列存入一维切片,再按每 c 个元素切分出一行。这种方案思路更直白("先读出来、再切回去"),但会额外占用 O(m×n) 的空间;仓库的双游标方案在空间上更优。两种方案的时间复杂度相同,读者可依据自己对可读性与空间开销的偏好选择。
复杂度分析
| 维度 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(m×n) |
需要遍历原矩阵的全部元素各一次,与重塑后的尺寸 r×c 相等 |
| 空间复杂度 | O(r×c) |
需要分配新矩阵存储结果;双游标方案无需额外的一维中转数组 |
| 非法情况 | O(1) |
元素总数不等时直接返回原矩阵,零拷贝 |
对于 m, n ≤ 100 的数据范围,最坏情况也只需处理 10,000 个元素,性能完全无压力,这也是 README 中将其称为"水题"的原因。
测试用例与验证
仓库在 566. Reshape the Matrix_test.go 中采用本仓库统一的"结构体参数化用例"风格组织测试,用 para566 封装输入参数(nums、r、c),用 ans566 封装期望输出:
qs := []question566{
{para566{[][]int{{1, 2}, {3, 4}}, 1, 4}, ans566{[][]int{{1, 2, 3, 4}}}},
{para566{[][]int{{1, 2}, {3, 4}}, 2, 4}, ans566{[][]int{{1, 2}, {3, 4}}}},
{para566{[][]int{{1, 2, 3, 4}}, 2, 2}, ans566{[][]int{{1, 2}, {3, 4}}}},
}
三个用例覆盖了三种典型场景:
| 用例 | 输入 | 期望输出 | 验证点 |
|---|---|---|---|
| 用例 1 | [[1,2],[3,4]], r=1, c=4 |
[[1,2,3,4]] |
2×2 → 1×4 展平成功 |
| 用例 2 | [[1,2],[3,4]], r=2, c=4 |
[[1,2],[3,4]] |
元素总数不等(4≠8),返回原矩阵 |
| 用例 3 | [[1,2,3,4]], r=2, c=2 |
[[1,2],[3,4]] |
1×4 → 2×2 折叠成功 |
其中用例 3 恰好验证了"行遍历顺序"的正确性:虽然目标形状不同,但元素仍按 1,2,3,4 的顺序逐行落位,与示例 2 互为逆操作。用例 2 则单独验证了非法输入时原样返回的兜底逻辑。
运行方式上,可进入对应题解目录执行 go test 验证本用例,也可以使用仓库根目录的 gotest.sh 脚本对整个 leetcode 包做覆盖率测试:该脚本通过 go test -covermode=atomic -coverprofile=coverage.txt ./leetcode/... 一次性对多个包生成单一合法的覆盖率文件。仓库项目的 README 声称 100% test coverage,本题的测试结构正是这一质量标准的缩影。
边界情况与易错点总结
- 元素总数不匹配:
m×n != r×c时必须返回原矩阵,这是最容易遗漏的分支,仓库通过canReshape单独抽出处理; - 空矩阵:题目约束矩阵高宽在
[1, 100],所以实现中len(nums[0])是安全的;若脱离本题约束自行扩展,需要先判断len(nums) == 0; - 单行 / 单列互转:1×N 与 M×1 的互转(如用例 3)容易出错,验证时建议把这类"形状反转"用例加入测试;
- 填充游标的换行时机:写入元素之前判断
colIndex == c还是之后判断,决定了游标复位逻辑的写法。仓库采用"写前判断、写后自增",读者可对比"写后判断"两种写法体会差异,避免出现列索引越界或漏换行的问题。
小结
LeetCode 566「重塑矩阵」的核心只有两件事:元素总数守恒是重塑合法的充要条件,行遍历顺序是填充的唯一次序依据。LeetCode-Go 仓库用 matrixReshape / canReshape / reshape 三个函数将其拆解为"入口调度 + 可行性判定 + 游标填充",并以双游标方案免去一维中转数组,是一份兼顾可读性与空间效率的参考实现。若想进一步查看本题完整代码与测试,可继续阅读 题目文档、解法实现 与 测试文件。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust4.21 K637- DDeepSeek-V4.1-FlashDeepSeek-V4.1-Flash 是一个多模态混合专家(MoE)模型,拥有 5520 亿骨干参数,并支持最多一百万 token 的上下文长度。该模型原生支持图像和文本输入,并以自回归方式生成文本Python270
cherry-studio🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端TypeScript2 K146
hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程Python46066
new-apiAI模型聚合管理中转分发系统,一个应用管理您的所有AI模型,支持将多种大模型转为统一格式调用,支持OpenAI、Claude、Gemini等格式,可供个人或者企业内部管理与分发渠道使用。🍥 A Unified AI Model Management & Distribution System. Aggregate all your LLMs into one app and access them via an OpenAI-compatible API, with native support for Claude (Messages) and Gemini formats.Go20143
JeecgBoot🔥企业级低代码平台集成了AI应用平台,帮助企业快速实现低代码开发和构建AI应用!前后端分离架构 SpringBoot,SpringCloud、Mybatis,Ant Design4、 Vue3.0、TS+vite!强大的代码生成器让前后端代码一键生成,无需写任何代码! 引领AI低代码开发模式: AI生成->OnlineCoding-> 代码生成-> 手工MERGE,显著的提高效率,又不失灵活~Java34051