LeetCode 566|重塑矩阵 Reshape the Matrix
费曼学习法核心思路:不用术语,用大白话讲清楚,假设你要讲给一个小白听懂;先明白问题本质,再思考方案,最后写代码,每行加详细注释
题目信息
题号:566
题目名称:重塑矩阵 Reshape the Matrix
难度:简单
标签:数组、矩阵、模拟
题目叙述
在 MATLAB 里面有个 reshape 函数,可以把一个 m × n 的二维矩阵,改成 r × c 的新矩阵,元素顺序保持不变。
现在给你二维矩阵 mat,还有两个正整数 r(目标行数)、c(目标列数)。
规则:
示例1
输入:mat = [[1,2],[3,4]], r = 1, c = 4
原矩阵是2行2列,总元素4个。目标1行4列,总数相等。
行遍历顺序:1, 2, 3, 4
输出:[[1,2,3,4]]
示例2
输入:mat = [[1,2],[3,4]], r = 2, c = 4
原总数4,目标总数8,数量不匹配,不能重塑。
输出:[[1,2],[3,4]]
约束
- m 和 n 的范围:1 <= m, n <= 100
- r,c 都是正整数
费曼讲解:破解思路(3种解法,从最简单到最优)
费曼学习法:把复杂问题拆成普通人能听懂的比喻
比喻:矩阵就像一盒排成多行多列的乒乓球。重塑=把所有乒乓球全部倒出来排成一条长队,再重新装到新盒子里。只有乒乓球总数一样,才能重装;总数不一样,不能重装,原样不动。
核心数学知识点(本题灵魂)
对于二维矩阵,行优先展开成一维序号 k(从0开始)
- 原矩阵 m行n列:
原行号:k // n (整数除法,向下取整)
原列号:k % n (取余数) - 新矩阵 r行c列:
新行号:k // c
新列号:k % c
举例子:原矩阵2行2列,k=2
k//n = 2//2 =1 第1行;k%n=2%2=0 第0列 → mat[1][0]=3 ✔
解法1:扁平化中转法(最容易理解,推荐新手)
思路:
Python代码(逐行最大注释)
from typing import List
class Solution:
def matrixReshape(self, mat: List[List[int]], r: int, c: int) –> List[List[int]]:
# mat:输入二维矩阵;r目标行数;c目标列数;返回重塑后的二维矩阵
# 第一步:获取原矩阵行数 m
m = len(mat)
# 获取原矩阵第0行的长度,也就是原矩阵列数 n
n = len(mat[0])
# 判断总元素数量是否相等,不等直接返回原矩阵,无法重塑
if m * n != r * c:
return mat
# 创建一维扁平列表,用来存放所有元素(把矩阵摊平成一条线)
flat_list = []
# 外层循环:遍历原矩阵每一行
for row in mat:
# 内层循环:遍历当前行里面每一个数字
for num in row:
# 将数字追加到扁平一维列表尾部
flat_list.append(num)
# 创建最终结果二维列表
result = []
# 初始化一维列表的读取指针,从第0个元素开始读取
ptr = 0
# 循环r次,每次构建新矩阵的一行
for i in range(r):
# 临时单行列表,用来存放新矩阵当前行的c个元素
temp_row = []
# 循环c次,填充当前行每一列
for j in range(c):
# 取出扁平列表ptr位置元素,放到临时行
temp_row.append(flat_list[ptr])
# 指针向后移动一位,准备读取下一个元素
ptr += 1
# 把构建完成的单行,加入最终结果矩阵
result.append(temp_row)
# 返回重塑完成的矩阵
return result
费曼讲解解法1
就像把盒子里所有乒乓球全部倒出来排成一队,然后拿新盒子,一行一行放进去。
优点:逻辑直观,人脑好理解;缺点:多开了一个一维数组,额外占用内存。时间复杂度 O(m*n),要遍历全部元素。
解法2:双循环遍历+坐标指针(原地填充,不摊平数组)
思路:
from typing import List
class Solution:
def matrixReshape(self, mat: List[List[int]], r: int, c: int) –> List[List[int]]:
# 获取原矩阵行数m
m = len(mat)
# 获取原矩阵列数n
n = len(mat[0])
# 元素总数不一致,无法重塑,返回原矩阵
if m * n != r * c:
return mat
# 创建r行c列初始值全0的二维列表
res = [[0] * c for _ in range(r)]
# k是全局一维序号指针,从0开始计数
k = 0
# 外层循环遍历原矩阵每一行
for i in range(m):
# 内层循环遍历原矩阵当前行每一列
for j in range(n):
# 计算当前元素在新矩阵的行号:k整除c
new_row = k // c
# 计算当前元素在新矩阵的列号:k对c取余数
new_col = k % c
# 将原矩阵 mat[i][j] 的值,写入新矩阵对应位置
res[new_row][new_col] = mat[i][j]
# 序号+1,处理下一个元素
k += 1
return res
费曼讲解解法2:不倒出乒乓球,一边读旧盒子,一边往新盒子里面放,只需要一个计数器标记这是第几个球。节省了一维数组空间。
解法3:数学索引映射最优单循环(推荐面试写)
最优写法:只用一层循环,直接用一维序号k同时映射原矩阵坐标、新矩阵坐标,代码最简洁。面试首选。
from typing import List
class Solution:
def matrixReshape(self, mat: List[List[int]], r: int, c: int) –> List[List[int]]:
# m:原矩阵行数
m = len(mat)
# n:原矩阵列数
n = len(mat[0])
# 总数校验,数量不一致直接返回原矩阵
if m * n != r * c:
return mat
# 初始化r行c列全0矩阵
res = [[0] * c for _ in range(r)]
# 一维序号k从0遍历到总元素mn-1,一层循环搞定
for k in range(m * n):
# 原矩阵行:k除以原列数n,整数除法
old_row = k // n
# 原矩阵列:k对原列数n取模
old_col = k % n
# 新矩阵行:k除以目标列数c,整数除法
new_row = k // c
# 新矩阵列:k对目标列数c取模
new_col = k % c
# 元素拷贝
res[new_row][new_col] = mat[old_row][old_col]
return res
费曼讲解解法3:
想象所有乒乓球编号0、1、2、3……。不管旧盒子还是新盒子,同一个编号的球,都可以通过「整除行数、取余列数」算出它在盒子里的位置。不需要两层循环,一个循环就能完成映射。
时间复杂度 O(mn),空间 O(r*c)(输出矩阵必须占用,不算额外开销)。
应用场景举例
这个LeetCode题目本质就是复刻 Numpy / MATLAB 的 reshape 底层逻辑。
测试代码(直接运行验证)
# 测试样例1
sol = Solution()
mat1 = [[1,2],[3,4]]
print(sol.matrixReshape(mat1, 1, 4)) # [[1,2,3,4]]
# 测试样例2
print(sol.matrixReshape(mat1,2,4)) # [[1,2],[3,4]]
网硕互联帮助中心


评论前必须登录!
注册