题目重点:遇原始零将整行整列归零,复用矩阵边界作标记并避免新零继续传播。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练可变矩形,各行不是同一list别名;原始值0触发置零,负数等其他值保持除受影响位置。空矩阵原样返回。
解题路线
- 先保存原首行有零与原首列有零两个bool。内区间发现原0,把matrix[r][0]与matrix[0][c]置0作为行列标记。
- 随后只遍历内区间,根据行/列标記清零。首行首列最后依据两个原始bool清零,不能提前清使所有标记变0。
- 不立即把发现0的整行整列清掉,否则后来读取的新0会继续触发,可能清空本不应受影响的全矩阵。标记阶段与应用阶段分开才保留原始集合。
- 首格[0][0]不能同时编码首行与首列独立状态,因此两个bool必要。单行/单列内循环为空,最后边界处理仍正确。
手动走一遍
[[1,2,3],[4,0,6],[7,8,9]]得到[[1,0,3],[0,0,0],[7,0,9]],不是全0。首行有零仅代表该行全部清,并列标记影响对应列,不代表首列也全部清。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
def set_zeroes(matrix):
"""Mutable rectangular matrix, original zeros clear whole row and column."""
if not matrix or not matrix[0]:return matrix
rows,cols=len(matrix),len(matrix[0])
if any(len(row)!=cols for row in matrix):raise ValueError('ragged matrix')
first_row=any(v==0 for v in matrix[0]);first_col=any(matrix[r][0]==0 for r in range(rows))
for r in range(1,rows):
for c in range(1,cols):
if matrix[r][c]==0:matrix[r][0]=0;matrix[0][c]=0
for r in range(1,rows):
for c in range(1,cols):
if matrix[r][0]==0 or matrix[0][c]==0:matrix[r][c]=0
if first_row:
for c in range(cols):matrix[0][c]=0
if first_col:
for r in range(rows):matrix[r][0]=0
return matrix
复杂度
O(RC)时间/O(1)算法辅助空间,输入矩阵即输出;若先copy输入返回新矩阵则总额外O(RC)。检验按原始零行/列集合构造oracle而非再实现同样marker逻辑。
易错点与追问
容易失分的边界
- 标记未完成就清首行首列,所有行列都被误标。
- [0][0]一个值混合两个独立边界状态。
继续深挖
- 如果不能改输入,怎样用row/col集合输出新矩阵并计空间?
- 矩阵行对象共享别名时为什么原地逐行修改会跨行影响?
