1. 矩阵遍历基础与LeetCode刷题指南
矩阵遍历是算法学习中最基础却至关重要的技能,就像建筑工人必须熟练掌握砌砖才能盖楼一样。我在ACM竞赛和硅谷大厂面试中深刻体会到,90%的二维矩阵类算法题都建立在四种基础遍历方法之上。本文将分享如何系统掌握这些技巧,并高效应用到LeetCode刷题中。
刚接触矩阵问题时,很多人会陷入"一看就会,一写就废"的困境。比如明明知道DFS可以解决岛屿问题,但实现时总是出现数组越界或死循环。这通常是因为对遍历的边界条件和访问顺序理解不够透彻。通过本文的图示分解和实战代码,你将建立起清晰的解题框架。
2. 矩阵遍历的四种核心模式
2.1 顺序遍历法
这是最直观的遍历方式,就像读书时从左到右、从上到下逐行阅读。以LeetCode 867(矩阵转置)为例:
def transpose(matrix): rows, cols = len(matrix), len(matrix[0]) res = [[0]*rows for _ in range(cols)] for i in range(rows): for j in range(cols): res[j][i] = matrix[i][j] # 行列坐标互换 return res关键点在于:
- 外层循环控制行号,内层循环控制列号
- 转置操作本质是行列索引交换
- 新建矩阵的维度是原矩阵的逆(cols×rows)
注意:Python中不要用
[[0]*rows]*cols初始化矩阵,这会导致行间引用问题
2.2 螺旋遍历法
类似蛇形填数,常用于LeetCode 54(螺旋矩阵)这类题目。核心是维护四个边界:
def spiralOrder(matrix): res = [] top, bottom = 0, len(matrix)-1 left, right = 0, len(matrix[0])-1 while top <= bottom and left <= right: # 从左到右 for j in range(left, right+1): res.append(matrix[top][j]) top += 1 # 从上到下 for i in range(top, bottom+1): res.append(matrix[i][right]) right -= 1 if top <= bottom: # 防止单行重复 # 从右到左 for j in range(right, left-1, -1): res.append(matrix[bottom][j]) bottom -= 1 if left <= right: # 防止单列重复 # 从下到上 for i in range(bottom, top-1, -1): res.append(matrix[i][left]) left += 1 return res2.3 对角线遍历
LeetCode 498(对角线遍历)展示了这种特殊遍历方式。关键在于发现:
- 奇数对角线方向为右上到左下
- 偶数对角线方向为左下到右上
def findDiagonalOrder(matrix): if not matrix: return [] m, n = len(matrix), len(matrix[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(matrix[i][j]) i -= 1 j += 1 else: # 奇数对角线 j = min(s, n-1) i = s - j while j >= 0 and i < m: res.append(matrix[i][j]) i += 1 j -= 1 return res2.4 BFS/DFS遍历
图论算法在矩阵中的应用,典型题目如LeetCode 200(岛屿数量):
def numIslands(grid): count = 0 for i in range(len(grid)): for j in range(len(grid[0])): if grid[i][j] == '1': count += 1 self.dfs(grid, i, j) return count def dfs(grid, i, j): if i<0 or j<0 or i>=len(grid) or j>=len(grid[0]) or grid[i][j]!='1': return grid[i][j] = '0' # 标记为已访问 dfs(grid, i+1, j) dfs(grid, i-1, j) dfs(grid, i, j+1) dfs(grid, i, j-1)3. 矩阵遍历的六大经典问题
3.1 旋转图像(LeetCode 48)
解法一:转置+镜像
def rotate(matrix): n = len(matrix) # 转置 for i in range(n): for j in range(i+1, n): matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] # 水平翻转 for i in range(n): matrix[i] = matrix[i][::-1]解法二:分层旋转(更符合题意)
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] = top3.2 矩阵置零(LeetCode 73)
O(1)空间复杂度解法:
def setZeroes(matrix): m, n = len(matrix), len(matrix[0]) first_row_has_zero = any(matrix[0][j] == 0 for j in range(n)) first_col_has_zero = any(matrix[i][0] == 0 for i in range(m)) # 使用第一行和第一列作为标记 for i in range(1, m): for j in range(1, n): if matrix[i][j] == 0: matrix[i][0] = 0 matrix[0][j] = 0 # 根据标记置零 for i in range(1, m): for j in range(1, n): if matrix[i][0] == 0 or matrix[0][j] == 0: matrix[i][j] = 0 # 处理第一行和第一列 if first_row_has_zero: for j in range(n): matrix[0][j] = 0 if first_col_has_zero: for i in range(m): matrix[i][0] = 03.3 搜索二维矩阵(LeetCode 240)
Z字形搜索法:
def searchMatrix(matrix, target): if not matrix: return False m, n = len(matrix), len(matrix[0]) row, col = 0, n-1 # 从右上角开始 while row < m and col >= 0: if matrix[row][col] == target: return True elif matrix[row][col] < target: row += 1 # 排除当前行 else: col -= 1 # 排除当前列 return False4. 矩阵遍历的优化技巧
4.1 方向数组的妙用
在DFS/BFS遍历中,使用方向数组可以简化代码:
# 四方向 directions = [(-1,0),(1,0),(0,-1),(0,1)] # 八方向 directions_8 = [(-1,-1),(-1,0),(-1,1), (0,-1), (0,1), (1,-1), (1,0), (1,1)] def dfs(grid, i, j): grid[i][j] = '0' # 标记访问 for di, dj in directions: ni, nj = i+di, j+dj if 0<=ni<len(grid) and 0<=nj<len(grid[0]) and grid[ni][nj]=='1': dfs(grid, ni, nj)4.2 虚拟边界技术
处理边界条件时,可以在矩阵外围添加虚拟边界:
def islandPerimeter(grid): m, n = len(grid), len(grid[0]) dummy_grid = [[0]*(n+2)] + [[0]+row+[0] for row in grid] + [[0]*(n+2)] perimeter = 0 for i in range(1, m+1): for j in range(1, n+1): if dummy_grid[i][j] == 1: perimeter += 4 - (dummy_grid[i-1][j] + dummy_grid[i+1][j] + dummy_grid[i][j-1] + dummy_grid[i][j+1]) return perimeter4.3 记忆化遍历
对于需要重复计算的路径问题,使用记忆化存储中间结果:
def longestIncreasingPath(matrix): if not matrix: return 0 m, n = len(matrix), len(matrix[0]) memo = [[0]*n for _ in range(m)] max_len = 0 def dfs(i, j): if memo[i][j] != 0: return memo[i][j] max_depth = 1 for di, dj in directions: ni, nj = i+di, j+dj if 0<=ni<m and 0<=nj<n and matrix[ni][nj]>matrix[i][j]: max_depth = max(max_depth, 1 + dfs(ni, nj)) memo[i][j] = max_depth return max_depth for i in range(m): for j in range(n): max_len = max(max_len, dfs(i, j)) return max_len5. 矩阵类题目刷题路线
5.1 新手必刷题单
- 基础遍历:566(重塑矩阵)、661(图片平滑器)
- 旋转镜像:48(旋转图像)、151(反转字符串里的单词)
- 搜索问题:74(搜索二维矩阵)、240(搜索二维矩阵II)
- 动态规划:64(最小路径和)、221(最大正方形)
- DFS/BFS:200(岛屿数量)、542(01矩阵)
5.2 进阶挑战题单
- 复杂遍历:329(矩阵中的最长递增路径)、407(接雨水II)
- 数学技巧:54(螺旋矩阵)、59(螺旋矩阵II)
- 综合应用:85(最大矩形)、212(单词搜索II)
5.3 大厂高频考题
- 字节跳动:48(旋转图像)、200(岛屿数量)
- 谷歌:54(螺旋矩阵)、329(最长递增路径)
- 亚马逊:733(图像渲染)、994(腐烂的橘子)
6. 调试技巧与常见错误
6.1 矩阵遍历的三大陷阱
- 索引越界:始终检查i±1/j±1是否在[0, len-1]范围内
- 重复访问:DFS中忘记标记已访问会导致死循环
- 浅拷贝问题:Python中用
new = matrix[:]只能浅拷贝一维列表
6.2 调试可视化技巧
打印矩阵的辅助函数:
def print_matrix(matrix): for row in matrix: print(' '.join(f'{num:3}' for num in row)) print('-'*20)6.3 边界条件检查清单
- 空矩阵输入:
[]或[[]] - 单行/单列矩阵:
[[1,2,3]]或[[1],[2],[3]] - 极值测试:100×100的矩阵,元素全相同
- 特殊值:包含负数、零、大整数的情况
我在刷题过程中总结出一个有效方法:遇到矩阵问题时,先在纸上画出3×3的示例矩阵,手动模拟算法流程。这能帮助快速发现逻辑漏洞,比直接写代码调试效率高得多。