Leetcode刷题记录29——矩阵置零
题源:https://leetcode.cn/problems/set-matrix-zeroes/description/?envType=study-plan-v2&envId=top-100-liked
题目描述:
思路一:
💡 解题思路
本题中我们采用如下策略:
- 第一次遍历整个矩阵,记录所有值为 0 的元素的 行号 和 列号。
- 使用两个集合
x_set
和y_set
分别保存需要置零的行索引和列索引。 - 第二次分别对这些行和列进行置零操作。
虽然这种方法没有做到最优的空间复杂度(O(1)),但它逻辑清晰、易于理解,是非常实用的一种实现方式。
✅ 具体实现步骤
第一步:获取矩阵尺寸
m = len(matrix) # 行数
n = len(matrix[0]) # 列数
首先我们通过 len()
获取矩阵的行数和列数,便于后续遍历。
第二步:记录所有需要置零的行和列
x_set = set() # 存储需要置零的行
y_set = set() # 存储需要置零的列for i in range(m):for j in range(n):if matrix[i][j] == 0:x_set.add(i)y_set.add(j)
这里我们使用了两个集合来记录出现 0 的行和列,这样可以避免重复处理。
⚠️ 注意:使用集合是为了自动去重,避免多次添加相同的行或列。
第三步:将对应的行全部置为 0
for i in x_set:for j in range(n):matrix[i][j] = 0
对每一个需要置零的行 i,我们从第 0 列到第 n-1 列依次设置为 0。
第四步:将对应的列全部置为 0
for j in y_set:for i in range(m):matrix[i][j] = 0
同理,对每一个需要置零的列 j,我们从第 0 行到第 m-1 行依次设置为 0。
⏱️ 时间与空间复杂度分析
时间复杂度:O(m * n),每个元素最多被访问两次
空间复杂度:O(m + n),使用了两个集合存储行和列
✅ 优点:逻辑清晰、实现简单
❌ 缺点:未达到 O(1) 空间复杂度(进阶可优化)
代码如下:
class Solution(object):def setZeroes(self, matrix):""":type matrix: List[List[int]]:rtype: None Do not return anything, modify matrix in-place instead."""zero_index = []m = len(matrix) # 行数n = len(matrix[0]) # 列数x_set= set()y_set = set()for i in range(m):for j in range(n):if matrix[i][j] == 0:x_set.add(i)y_set.add(j)# 将对应的行置零for i in x_set:for j in range(n):matrix[i][j] = 0# 将对应的列置零for j in y_set:for i in range(m):matrix[i][j] = 0
执行时间如下:
思路二:
核心思想:
利用矩阵的第一行和第一列来标记对应的列和行是否需要置零,这样就不需要额外的空间了。
但需要注意的是,第一行和第一列本身也可能被置零,所以我们需要用两个布尔变量来单独记录它们的状态。
✅ 具体实现步骤
第一步:初始化标记变量
first_row_has_zero = False # 是否第一行需要置零
first_col_has_zero = False # 是否第一列需要置零
这两个变量用于最后判断是否要将第一行或第一列整体置零。
第二步:检查第一列中是否有 0
for i in range(m):if matrix[i][0] == 0:first_col_has_zero = Truebreak
只要第一列中有一个 0,说明整列都要置零。
第三步:检查第一行中是否有 0
for j in range(n):if matrix[0][j] == 0:first_row_has_zero = Truebreak
同理,只要第一行中有 0,就说明整行要置零。
第四步:使用第一行和第一列作为标记数组
从第二行、第二列开始遍历整个矩阵:
for i in range(1, m):for j in range(1, n):if matrix[i][j] == 0:matrix[i][0] = 0matrix[0][j] = 0
如果当前元素是 0,就把该行首和列首置零,表示这一行/列后续都需要置零。
第五步:根据标记置零中间部分
for i in range(1, m):if matrix[i][0] == 0:for j in range(1, n):matrix[i][j] = 0for j in range(1, n):if matrix[0][j] == 0:for i in range(1, m):matrix[i][j] = 0
- 对于每一行,如果它的第一个元素是 0,则整行置零;
- 对于每一列,如果它的第一个元素是 0,则整列置零。
第六步:处理第一行和第一列
if first_row_has_zero:for j in range(n):matrix[0][j] = 0if first_col_has_zero:for i in range(m):matrix[i][0] = 0
最后再根据最开始保存的两个布尔值来决定是否把第一行和第一列也置零。
⏱️ 时间与空间复杂度分析
时间复杂度:O(m * n),每个元素最多被访问两次
空间复杂度:O(1) ,只使用了常数级额外空间
代码如下:
class Solution(object):def setZeroes(self, matrix):""":type matrix: List[List[int]]:rtype: None Do not return anything, modify matrix in-place instead."""m = len(matrix) # 行数n = len(matrix[0]) # 列数first_row_has_zero = False # 是否第一行需要置零first_col_has_zero = False # 是否第一列需要置零# 检查第一列是否有 0for i in range(m):if matrix[i][0] == 0:first_col_has_zero = Truebreak# 检查第一行是否有 0for j in range(n):if matrix[0][j] == 0:first_row_has_zero = Truebreak# 使用第一行和第一列作为标记数组for i in range(1, m):for j in range(1, n):if matrix[i][j] == 0:matrix[i][0] = 0matrix[0][j] = 0# 根据标记置零中间部分for i in range(1, m):if matrix[i][0] == 0:for j in range(1, n):matrix[i][j] = 0for j in range(1, n):if matrix[0][j] == 0:for i in range(1, m):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] = 0
执行时间如下: