1. 矩阵高频算法题深度解析
在算法面试和编程竞赛中,矩阵类题目因其考察维度多、解法灵活而成为必考重点。今天我将拆解LeetCode上四道经典矩阵题目(73、54、48、240),分享从暴力解法到最优解的完整思考路径。这些题目覆盖了矩阵遍历、原地操作、数学性质应用等核心考点,是Google、Meta等大厂面试的常客。
2. LeetCode 73:矩阵置零
2.1 问题描述
给定一个m×n矩阵,若某元素为0,则将其所在行和列的所有元素都设为0。要求使用原地算法。
2.2 暴力解法改进
最直观的做法是新建标记矩阵,但空间复杂度O(mn)显然不理想。优化后可改用两个标记数组分别记录行和列:
python复制def setZeroes(matrix):
rows = [False] * len(matrix)
cols = [False] * len(matrix[0])
for i in range(len(matrix)):
for j in range(len(matrix[0])):
if matrix[i][j] == 0:
rows[i] = True
cols[j] = True
for i in range(len(matrix)):
for j in range(len(matrix[0])):
if rows[i] or cols[j]:
matrix[i][j] = 0
注意:Python中布尔数组初始化时要用False而非0,避免与数值混淆
2.3 最优解:原地标记法
利用矩阵第一行和第一列作为标记空间:
- 先检查第一行/列是否需要置零
- 用第一行记录各列是否有零,第一列记录各行是否有零
- 根据标记处理除第一行/列外的元素
- 最后处理第一行/列
python复制def setZeroes(matrix):
first_row_zero = any(x == 0 for x in matrix[0])
first_col_zero = any(matrix[i][0] == 0 for i in range(len(matrix)))
for i in range(1, len(matrix)):
for j in range(1, len(matrix[0])):
if matrix[i][j] == 0:
matrix[i][0] = 0
matrix[0][j] = 0
for i in range(1, len(matrix)):
for j in range(1, len(matrix[0])):
if matrix[i][0] == 0 or matrix[0][j] == 0:
matrix[i][j] = 0
if first_row_zero:
for j in range(len(matrix[0])):
matrix[0][j] = 0
if first_col_zero:
for i in range(len(matrix)):
matrix[i][0] = 0
3. LeetCode 54:螺旋矩阵
3.1 问题描述
给定m×n矩阵,按顺时针螺旋顺序返回所有元素。
3.2 层级遍历法
将矩阵看作由外到内的多层洋葱结构:
python复制def spiralOrder(matrix):
if not matrix: return []
res = []
top, bottom = 0, len(matrix)-1
left, right = 0, len(matrix[0])-1
while True:
# 从左到右
for j in range(left, right+1):
res.append(matrix[top][j])
top += 1
if top > bottom: break
# 从上到下
for i in range(top, bottom+1):
res.append(matrix[i][right])
right -= 1
if left > right: break
# 从右到左
for j in range(right, left-1, -1):
res.append(matrix[bottom][j])
bottom -= 1
if top > bottom: break
# 从下到上
for i in range(bottom, top-1, -1):
res.append(matrix[i][left])
left += 1
if left > right: break
return res
3.3 方向向量法
使用方向向量模拟螺旋路径:
python复制def spiralOrder(matrix):
if not matrix: return []
directions = [(0,1),(1,0),(0,-1),(-1,0)]
m, n = len(matrix), len(matrix[0])
visited = [[False]*n for _ in range(m)]
res = []
x, y = 0, 0
d = 0
for _ in range(m*n):
res.append(matrix[x][y])
visited[x][y] = True
dx, dy = directions[d]
nx, ny = x+dx, y+dy
if 0<=nx<m and 0<=ny<n and not visited[nx][ny]:
x, y = nx, ny
else:
d = (d+1)%4
dx, dy = directions[d]
x, y = x+dx, y+dy
return res
4. LeetCode 48:旋转图像
4.1 问题描述
将n×n二维矩阵顺时针旋转90度,要求原地修改。
4.2 数学规律
旋转本质是两次翻转操作:
- 先沿主对角线翻转(转置)
- 再水平翻转
python复制def rotate(matrix):
n = len(matrix)
# 转置
for i in range(n):
for j in range(i):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
# 水平翻转
for i in range(n):
matrix[i] = matrix[i][::-1]
4.3 分层旋转法
将矩阵分为n/2层,逐层旋转:
python复制def rotate(matrix):
n = len(matrix)
for layer in range(n//2):
first = layer
last = n - 1 - layer
for i in range(first, last):
offset = i - first
# 保存上边
top = matrix[first][i]
# 左到上
matrix[first][i] = matrix[last-offset][first]
# 下到左
matrix[last-offset][first] = matrix[last][last-offset]
# 右到下
matrix[last][last-offset] = matrix[i][last]
# 上到右
matrix[i][last] = top
5. LeetCode 240:搜索二维矩阵II
5.1 问题描述
在每行每列都按升序排列的m×n矩阵中,高效判断目标值是否存在。
5.2 二分搜索变种
从右上角开始搜索:
python复制def searchMatrix(matrix, target):
if not matrix: return False
m, n = len(matrix), len(matrix[0])
i, j = 0, n-1
while i < m and j >= 0:
if matrix[i][j] == target:
return True
elif matrix[i][j] > target:
j -= 1
else:
i += 1
return False
5.3 分治法优化
将矩阵划分为四个象限:
python复制def searchMatrix(matrix, target):
def search_rec(left, up, right, down):
if left > right or up > down:
return False
elif target < matrix[up][left] or target > matrix[down][right]:
return False
mid = left + (right-left)//2
row = up
while row <= down and matrix[row][mid] <= target:
if matrix[row][mid] == target:
return True
row += 1
return search_rec(left, row, mid-1, down) or search_rec(mid+1, up, right, row-1)
if not matrix: return False
return search_rec(0, 0, len(matrix[0])-1, len(matrix)-1)
6. 矩阵问题通用技巧
6.1 边界条件处理
- 空矩阵检查(m=0或n=0)
- 单行/单列矩阵的特殊处理
- 方阵与非方阵的区别
6.2 复杂度优化方向
- 空间优化:尽量使用输入矩阵作为存储空间
- 时间优化:利用矩阵有序性进行二分搜索
- 并行计算:某些操作可分解为独立子任务
6.3 调试技巧
- 打印中间状态矩阵
- 使用小规模测试用例(如2×2矩阵)
- 验证矩阵不变式(如旋转后行列对应关系)
在面试中遇到矩阵问题时,建议先明确问题边界,再选择适合的遍历方式(行优先/列优先/对角线),最后考虑数学性质优化。实际编码时要注意Python中列表的浅拷贝问题,对矩阵进行操作时建议先复制一份原始数据。
