73. 矩阵置零
python
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
m, n = len(matrix), len(matrix[0])
first_col_zero = False
for i in range(m):
if matrix[i][0] == 0:
first_col_zero = True
for j in range(1, n):
if matrix[i][j] == 0:
matrix[i][0] = 0
matrix[0][j] = 0
for i in range(m - 1, -1, -1):
for j in range(n - 1, 0, -1):
if matrix[i][0] == 0 or matrix[0][j] == 0:
matrix[i][j] = 0
if first_col_zero:
matrix[i][0] = 0
-
初步思路标题:标记数组法
-
使用两个布尔数组分别记录需要置零的行和列
-
需要O(m+n)额外空间,不符合常数空间的要求
-
-
原地标记优化思路:
-
利用矩阵的第一行和第一列作为标记位,记录对应行和列是否需要置零
-
需要额外两个变量记录第一行和第一列本身是否包含0
-
从右下角开始遍历修改,避免标记位被提前覆盖
-
-
关键策略优势:
-
空间复杂度优化到O(1),只使用常数个额外变量
-
两次遍历即可完成标记和修改,时间复杂度O(m×n)
-
通过逆序遍历避免标记位被覆盖导致的错误
-
-
具体实现:
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
m, n = len(matrix), len(matrix[0])
first_col_zero = False # 标记第一列是否需要置零
# 第一次遍历:用第一行和第一列记录需要置零的行和列
for i in range(m):
# 检查第一列是否有0
if matrix[i][0] == 0:
first_col_zero = True
# 从第二列开始检查
for j in range(1, n):
if matrix[i][j] == 0:
matrix[i][0] = 0 # 标记第i行需要置零
matrix[0][j] = 0 # 标记第j列需要置零
# 第二次遍历:根据标记置零(除第一行第一列外)
for i in range(m - 1, -1, -1):
# 从最后一列到第二列
for j in range(n - 1, 0, -1):
if matrix[i][0] == 0 or matrix[0][j] == 0:
matrix[i][j] = 0
# 最后处理第一列
if first_col_zero:
matrix[i][0] = 0
54. 螺旋矩阵
python
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
dirs = [[0,1],[1,0],[0,-1],[-1,0]]
m, n = len(matrix), len(matrix[0])
ans = []
i, j, di = 0, 0, 0
for _ in range(m*n):
ans.append(matrix[i][j])
matrix[i][j] = None
x, y = i + dirs[di][0], j + dirs[di][1]
if x < 0 or x >= m or y < 0 or y >= n or matrix[x][y] is None:
di = (di + 1) % 4
i += dirs[di][0]
j += dirs[di][1]
return ans
-
初步思路标题:分层模拟法
-
按层模拟,逐层遍历矩阵的外圈到内圈
-
需要维护四个边界变量,处理边界收缩的逻辑较为复杂
-
-
方向数组+标记法优化思路:
-
使用方向数组表示四个移动方向(右、下、左、上)
-
访问过的元素标记为None,遇到边界或已访问元素时改变方向
-
通过取模运算循环切换方向,实现自动转向
-
-
关键策略优势:
-
代码简洁直观,避免复杂的边界判断和循环条件
-
通过标记法自动处理转向逻辑,无需显式维护边界
-
一次遍历即可完成,时间复杂度O(m×n)
-
-
具体实现:
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
# 方向数组:右、下、左、上
dirs = [[0,1],[1,0],[0,-1],[-1,0]]
m, n = len(matrix), len(matrix[0])
ans = []
i, j, di = 0, 0, 0 # 当前位置和方向索引
for _ in range(m*n):
ans.append(matrix[i][j])
matrix[i][j] = None # 标记已访问
# 尝试按当前方向移动
x, y = i + dirs[di][0], j + dirs[di][1]
# 如果下一个位置越界或已访问,改变方向
if x < 0 or x >= m or y < 0 or y >= n or matrix[x][y] is None:
di = (di + 1) % 4 # 顺时针转向下一方向
# 更新位置
i += dirs[di][0]
j += dirs[di][1]
return ans
48. 旋转图像
python
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
n = len(matrix)
for i in range(n):
for j in range(i, n):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
for row in matrix:
row.reverse()
-
初步思路:直接模拟旋转
-
最初可能想到创建一个新的矩阵,按照旋转规则将原矩阵的元素放到新矩阵的对应位置
-
这种方法的时间复杂度为O(n²),但空间复杂度也为O(n²),不满足题目要求的原地修改条件
-
-
优化思路:两次操作转化法
-
优化方向1:将复杂的旋转操作分解为两个简单的矩阵操作。顺时针旋转90度可以等价为先进行矩阵转置,然后对每一行进行反转
-
优化方向2:利用矩阵转置(行列互换)和对换行内元素的组合,通过简单的元素交换实现旋转,避免了复杂的坐标计算
-
-
关键策略优势:
-
策略带来的具体好处1:原地操作,只使用常数级别的额外空间,完全满足题目要求
-
策略带来的具体好处2:算法逻辑清晰简单,通过两个基础矩阵操作的组合,避免了复杂的元素移动计算
-
-
具体实现:
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
n = len(matrix)
# 第一步:矩阵转置(沿主对角线翻转)
# 只遍历上三角矩阵,将(i,j)与(j,i)交换
for i in range(n):
for j in range(i, n):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
# 第二步:每一行反转
# 对转置后的矩阵的每一行进行左右翻转
for row in matrix:
row.reverse()
240. 搜索二维矩阵 II
python
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
r, c = len(matrix), len(matrix[0])
i, j = 0, c - 1
while i < r and j >= 0:
if matrix[i][j] > target:
j -= 1
elif matrix[i][j] < target:
i += 1
else:
return True
return False
-
初步思路标题(暴力遍历法) :
最直接的解法是对整个矩阵进行双重循环遍历,比较每个元素是否等于目标值。这种方法虽然简单直观,但时间复杂度为O(m×n),没有利用矩阵的有序特性,在矩阵规模较大时效率很低。
-
优化思路标题(Z字形搜索法):
-
优化方向1:从矩阵的右上角(或左下角)开始搜索,利用矩阵的行列有序特性,每次比较可以排除一行或一列
-
优化方向2:通过控制行索引和列索引的移动方向,实现线性时间复杂度的搜索,无需使用二分查找或额外空间
-
-
关键策略优势:
-
策略带来的具体好处1:每次比较都能排除一整行或一整列,搜索路径最多只经过m+n个元素,时间复杂度降为O(m+n)
-
策略带来的具体好处2:仅使用两个指针变量,空间复杂度为O(1),且代码简洁易懂
-
-
具体实现:
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
# 获取矩阵的行数和列数
r, c = len(matrix), len(matrix[0])
# 从右上角开始搜索
i, j = 0, c - 1
# 当行索引在范围内且列索引在范围内时继续搜索
while i < r and j >= 0:
# 如果当前元素大于目标值,排除当前列
if matrix[i][j] > target:
j -= 1
# 如果当前元素小于目标值,排除当前行
elif matrix[i][j] < target:
i += 1
# 找到目标值
else:
return True
# 搜索完所有可能位置仍未找到
return False