1. 矩阵遍历:算法工程师的必修课
第一次刷Leetcode矩阵题时,我盯着那道"旋转图像"足足半小时无从下手。直到把矩阵拆解成行、列、对角线的组合关系,才突然理解二维数组遍历的本质差异。矩阵类题目在各大厂面试中出现频率高达67%(据2023年Leetcode企业题库统计),其核心考察点正是对二维数据结构的操作能力。
不同于线性表的一维思维,矩阵遍历需要建立坐标系意识。想象你站在一个围棋棋盘上,既要控制行进的步长(步频),又要处理转向的逻辑(状态机)。常见的四种基础遍历模式:逐行扫描、螺旋遍历、对角线遍历、旋转遍历,构成了90%矩阵题目的解题骨架。下面我们就用几个经典例题,拆解这些模式的实现细节与变形套路。
关键认知:矩阵问题本质是二维状态机,处理重点是坐标变换与边界判定
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础遍历模式深度解析
2.1 逐行扫描与层序遍历
最简单的54.螺旋矩阵(Spiral Matrix)展示了最基础的层序遍历思想。就像剥洋葱一样,我们一圈圈从外向内处理元素。这里有两个关键技巧:
- 使用四边界标记法(left/right/top/bottom)替代复杂的坐标计算
python复制def spiralOrder(matrix):
res = []
l, r, t, b = 0, len(matrix[0])-1, 0, len(matrix)-1
while l <= r and t <= b:
for i in range(l, r+1): res.append(matrix[t][i]) # 上边左→右
t += 1
for i in range(t, b+1): res.append(matrix[i][r]) # 右边上→下
r -= 1
if not (l <= r and t <= b): break # 奇数行偶数列情况
for i in range(r, l-1, -1): res.append(matrix[b][i]) # 下边右→左
b -= 1
for i in range(b, t-1, -1): res.append(matrix[i][l]) # 左边下→上
l += 1
return res
- 处理非方阵时的中断条件(第8行),避免重复遍历。实测在3×4矩阵中,缺少该判断会导致中心元素被重复添加。
2.2 对角线遍历的艺术
498.对角线遍历(Diagonal Traverse)展示了坐标和的妙用。观察发现:
- 奇数对角线方向为右上→左下,偶数方向相反
- 同一对角线上元素的行列索引和恒定
python复制def findDiagonalOrder(mat):
if not mat: return []
m, n = len(mat), len(mat[0])
res = []
for s in range(m + n - 1): # 对角线总数
if s % 2 == 0: # 偶数对角线
i = min(s, m-1)
j = s - i
while i >= 0 and j < n:
res.append(mat[i][j])
i -= 1
j += 1
else: # 奇数对角线
j = min(s, n-1)
i = s - j
while j >= 0 and i < m:
res.append(mat[i][j])
i += 1
j -= 1
return res
这里有个易错点:当对角线超出矩阵范围时(第7/13行的min操作),需要调整起点位置。建议在纸上画出3×4矩阵的遍历路径来理解坐标变化。
3. 高级变形与优化技巧
3.1 旋转遍历的坐标映射
48.旋转图像(Rotate Image)要求原地顺时针旋转90度。通过坐标推导可以发现:
- 顺时针旋转:mat[i][j] → mat[j][n-1-i]
- 逆时针旋转:mat[i][j] → mat[m-1-j][i]
分层旋转解法比直接映射更易实现:
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
实测陷阱:Python中直接使用matrix[:] = zip(*matrix[::-1])虽然简洁,但不符合"原地修改"要求,面试时会被扣分。
3.2 状态记录与剪枝
79.单词搜索(Word Search)展示了DFS遍历中的优化技巧。相比暴力搜索,有三大优化点:
- 使用#标记已访问路径,替代额外的visited矩阵
- 先检查首字母匹配再进入DFS,减少无效递归
- 使用any链式短路求值,找到即终止
python复制def exist(board, word):
def dfs(i, j, k):
if not 0<=i<m or not 0<=j<n or board[i][j]!=word[k]:
return False
if k == len(word)-1:
return True
board[i][j] = '#'
res = any(dfs(i+di,j+dj,k+1) for di,dj in [(1,0),(-1,0),(0,1),(0,-1)])
board[i][j] = word[k]
return res
m, n = len(board), len(board[0])
for i in range(m):
for j in range(n):
if board[i][j] == word[0] and dfs(i, j, 0):
return True
return False
4. 高频问题排查手册
4.1 索引越界经典场景
| 问题现象 | 产生原因 | 解决方案 |
|---|---|---|
| IndexError: list index out of range | 未考虑空矩阵或单行矩阵 | 前置条件检查 if not matrix or not matrix[0] |
| 螺旋遍历结果缺失 | 未处理非方阵的中断条件 | 添加内层循环后的边界检查 |
| 对角线遍历顺序错乱 | 奇偶对角线方向混淆 | 用s%2==0判断而非s%2==1 |
4.2 时间复杂度优化对比
| 题目 | 暴力解法 | 优化解法 | 核心技巧 |
|---|---|---|---|
| 73.矩阵置零 | O(mn)空间记录零位 | O(1)空间首行首列标记 | 利用矩阵自身存储状态 |
| 240.搜索二维矩阵II | O(mn)全遍历 | O(m+n)阶梯搜索 | 利用行列有序特性 |
| 329.矩阵中的最长递增路径 | 无记忆化DFS超时 | 记忆化搜索+DP | 存储已计算路径 |
4.3 Python特性注意事项
- 浅拷贝陷阱:旋转操作若用new_mat = mat[:]只是浅拷贝,二维数组需使用deepcopy
- 矩阵转置技巧:list(zip(*matrix))会返回tuple组成的list,需注意类型转换
- 原地修改要求:部分题目明确禁止创建新矩阵,需仔细阅读约束条件
5. 专题训练路线建议
根据周赛出现频率,建议按此顺序攻坚:
- 基础遍历:螺旋矩阵(54)→对角线遍历(498)→旋转图像(48)
- 搜索应用:单词搜索(79)→岛屿数量(200)→最大加号标志(764)
- 动态规划:最小路径和(64)→最长递增路径(329)→最大矩形(85)
- 数学技巧:矩阵置零(73)→生命游戏(289)→范围求和(598)
对于二维DP类题目,建议先在纸上画出状态转移图。例如221.最大正方形,定义dp[i][j]表示以(i,j)为右下角的最大边长,其状态转移取决于左、上、左上三个相邻点的最小值。
