一、前置知识在做这道题之前我们需要先掌握几个基础概念哪怕你完全没接触过算法、没学过矩阵看完这部分也能轻松跟上后续内容。1. 什么是矩阵矩阵就是“二维数组”简单说就是“表格”——有行、有列每个格子里放一个数字。比如题目中的输入matrix [[1,1,1],[1,0,1],[1,1,1]]就是一个 3 行 3 列的矩阵第 0 行第一行[1, 1, 1]第 1 行第二行[1, 0, 1]第 2 行第三行[1, 1, 1]这里要注意计算机里的“索引”行号、列号都是从 0 开始的不是从 1 开始的就像我们数数从 0 起步一样记好这个细节后面看代码不会乱。2. 矩阵的遍历怎么逐个看矩阵里的数字要处理矩阵里的每个数字需要用“双重循环”——外层循环管“行”内层循环管“列”。比如用i表示“当前行号”用j表示“当前列号”先固定i比如先看第 0 行然后让j从 0 到 列数-1看完第 0 行的所有列再让i加 1看第 1 行重复这个过程就能看完矩阵里所有的数字。举个例子遍历 3x3 矩阵顺序是(0,0) → (0,1) → (0,2) → (1,0) → (1,1) → (1,2) → (2,0) → (2,1) → (2,2)对应表格里的每个格子。3. 原地算法题目强制要求原地算法的核心不创建和原矩阵一样大的新矩阵直接修改题目给的原始矩阵。简单说题目给你一个矩阵matrix你不能再做一个和它一样大的矩阵比如new_matrix [[0]*n for _ in range(m)]来存结果必须直接改matrix里面的数字这样做的目的是节省内存。题目里函数返回值是NonePython或voidC就是告诉你“不用返回新矩阵改原始矩阵就行”。4. 空间复杂度衡量算法占用内存的多少空间复杂度是算法额外占用的内存大小数值越小越好这道题的进阶要求是“常量空间”占用内存固定和矩阵大小无关。我们分三种情况理解对应后面的三种解法O(mn)额外创建一个和原矩阵一样大的矩阵m 行 n 列比如暴力解法占用内存最多O(mn)额外创建两个一维数组一个存需要置零的行一个存需要置零的列占用内存中等O(1)只用到几个临时变量比如一个布尔值、一个整数不创建任何数组占用内存最少是最优解。补充m 是矩阵的行数n 是矩阵的列数比如 3x3 矩阵m3n3。5. Python 和 C 中二维数组的基础操作必看否则看不懂代码Python 部分获取矩阵行数m len(matrix)matrix 是一个列表里面的每个元素都是一行所以列表的长度就是行数获取矩阵列数n len(matrix[0])matrix[0] 是第一行第一行的长度就是每一行的列数矩阵每一行列数都一样访问矩阵中第 i 行第 j 列的元素matrix[i][j]复制矩阵temp [row[:] for row in matrix]row[:] 是复制每一行避免修改 temp 时影响原 matrix。C 部分获取矩阵行数int m matrix.size();matrix 是 vectorvectorint 类型size() 返回它的元素个数每个元素是一行获取矩阵列数int n matrix[0].size();matrix[0] 是第一行size() 返回第一行的元素个数即列数访问矩阵中第 i 行第 j 列的元素matrix[i][j]复制矩阵vectorvectorint temp matrix;vector 赋值会自动复制所有元素修改 temp 不影响原 matrixvector 初始化vectorbool row(m, false);创建一个长度为 m 的 bool 类型数组所有元素默认是 false。6. 关键坑点避免做错的核心绝对不能“边遍历边置零”比如你遍历到 matrix[i][j] 0直接把第 i 行、第 j 列都改成 0那么后面遍历到这些新改的 0 时会误以为它们是“原始的 0”导致不该置零的行和列也被置零最终结果错误。比如矩阵 [[1,1,1],[1,0,1],[1,1,1]]如果直接边遍历边置零当遇到 (1,1) 是 0把第 1 行、第 1 列置零后矩阵变成 [[1,0,1],[0,0,0],[1,0,1]]这时候再遍历 (0,1) 这个新的 0会把第 0 行、第 1 列再置零虽然结果对但逻辑错了换个矩阵就会出错。所以必须先“标记”需要置零的行和列再统一置零。二、题目解析再明确一遍题目要求给定一个 m x n 的矩阵二维数组如果矩阵中任意一个元素是 0就把这个元素所在的“整行”和“整列”的所有元素都改成 0。要求必须用原地算法不能创建新的大矩阵。示例 1输入matrix [[1,1,1],[1,0,1],[1,1,1]] → 有一个 0 在 (1,1) 位置输出[[1,0,1],[0,0,0],[1,0,1]] → 第 1 行全置零第 1 列全置零示例 2输入matrix [[0,1,2,0],[3,4,5,2],[1,3,1,5]] → 0 在 (0,0) 和 (0,3) 位置输出[[0,0,0,0],[0,4,5,0],[0,3,1,0]] → 第 0 行全置零第 0 列、第 3 列全置零三、三种解法从暴力到最优逐一看懂我们按照“暴力解法入门好懂→ 优化解法面试基础→ 最优解法面试进阶”的顺序讲解每种解法都包含核心思路、Python 代码逐行注释、C 代码逐行注释、运行流程跟着走、优缺点。解法一暴力解法O(mn) 空间入门首选核心思路既然不能边遍历边置零那我们就先“复制一份原始矩阵”遍历复制的矩阵temp遇到 0 就去修改“原始矩阵matrix”的对应行和列。这样复制的矩阵里的 0 都是原始的 0不会被新置的 0 干扰。步骤1. 复制原始矩阵 → 2. 遍历复制矩阵标记并修改原始矩阵 → 3. 完成置零。Python 代码逐行注释能看懂每一句from typing import List # 导入List类型用于指定matrix的类型可理解为告诉计算机matrix是二维列表 class Solution: def setZeroes(self, matrix: List[List[int]]) - None: Do not return anything, modify matrix in-place instead. 函数说明必看 - 这个函数不需要返回任何值返回None - 要求直接修改输入的matrix原地算法 - matrix: 输入的二维矩阵题目给的原始矩阵 # 1. 获取矩阵的行数m和列数n m len(matrix) # len(matrix)返回matrix的元素个数每个元素是一行所以m是行数 n len(matrix[0]) # matrix[0]是第一行len(matrix[0])是第一行的元素个数即列数n # 2. 复制原始矩阵temp用于后续遍历避免修改原矩阵时干扰判断 # row[:] 表示复制每一行的所有元素这样修改temp不会影响matrix temp [row[:] for row in matrix] # 3. 遍历复制的矩阵temp找所有的0然后修改原矩阵matrix # 外层循环遍历每一行i是当前行号从0到m-1 for i in range(m): # 内层循环遍历当前行的每一列j是当前列号从0到n-1 for j in range(n): # 当temp中当前位置i,j是0时说明原矩阵对应位置也是0需要置零对应行和列 if temp[i][j] 0: # 3.1 置零原矩阵的第i行把第i行所有列的元素都改成0 # k是列号从0到n-1逐个修改第i行的每个元素 for k in range(n): matrix[i][k] 0 # 把第i行第k列的元素改成0 # 3.2 置零原矩阵的第j列把第j列所有行的元素都改成0 # k是行号从0到m-1逐个修改第j列的每个元素 for k in range(m): matrix[k][j] 0 # 把第k行第j列的元素改成0 # 主函数测试代码可直接运行包含示例和自定义输入 if __name__ __main__: # 实例化Solution类可理解为创建一个“解题工具” solution Solution() # 测试示例1 print( 测试示例1 ) matrix1 [[1,1,1],[1,0,1],[1,1,1]] # 示例1输入 print(输入矩阵) # 打印输入矩阵逐行打印更直观 for row in matrix1: print(row) solution.setZeroes(matrix1) # 调用置零函数直接修改matrix1 print(输出矩阵) for row in matrix1: print(row) # 打印修改后的矩阵应该是[[1,0,1],[0,0,0],[1,0,1]] # 测试示例2 print(\n 测试示例2 ) matrix2 [[0,1,2,0],[3,4,5,2],[1,3,1,5]] # 示例2输入 print(输入矩阵) for row in matrix2: print(row) solution.setZeroes(matrix2) print(输出矩阵) for row in matrix2: print(row) # 打印修改后的矩阵应该是[[0,0,0,0],[0,4,5,0],[0,3,1,0]] # 自定义测试自己输入一个矩阵测试算法 print(\n 自定义测试 ) # 自定义一个4x4矩阵包含多个0 matrix3 [[1,2,3,4],[5,0,7,8],[9,10,11,12],[13,14,15,0]] print(输入矩阵) for row in matrix3: print(row) solution.setZeroes(matrix3) print(输出矩阵) for row in matrix3: print(row) # 预期输出[[1,0,3,0],[0,0,0,0],[9,0,11,0],[0,0,0,0]]C 代码逐行注释能看懂每一句#include vector // 导入vector容器可理解为用于创建数组的工具 #include iostream // 导入输入输出工具用于打印矩阵 using namespace std; // 简化代码不用每次写std:: class Solution { public: // 函数说明 // - void表示函数不返回任何值对应Python的None // - vectorvectorint matrix输入的二维矩阵表示“引用”修改matrix会直接修改原始矩阵原地算法 void setZeroes(vectorvectorint matrix) { // 1. 获取矩阵的行数m和列数n int m matrix.size(); // matrix.size()返回矩阵的行数vector的元素个数每个元素是一行 int n matrix[0].size(); // matrix[0].size()返回第一行的元素个数即列数n // 2. 复制原始矩阵temp用于后续遍历避免修改原矩阵时干扰判断 vectorvectorint temp matrix; // 直接赋值vector会自动复制所有元素 // 3. 遍历复制的矩阵temp找所有的0然后修改原矩阵matrix // 外层循环遍历每一行i是当前行号从0到m-1 for (int i 0; i m; i) { // 内层循环遍历当前行的每一列j是当前列号从0到n-1 for (int j 0; j n; j) { // 当temp中当前位置i,j是0时置零原矩阵的第i行和第j列 if (temp[i][j] 0) { // 3.1 置零原矩阵的第i行 for (int k 0; k n; k) { matrix[i][k] 0; // 把第i行第k列的元素改成0 } // 3.2 置零原矩阵的第j列 for (int k 0; k m; k) { matrix[k][j] 0; // 把第k行第j列的元素改成0 } } } } } }; // 主函数测试代码可直接运行包含示例和自定义输入 int main() { // 实例化Solution类创建解题工具 Solution solution; // 测试示例1 cout 测试示例1 endl; vectorvectorint matrix1 {{1,1,1},{1,0,1},{1,1,1}}; // 示例1输入 cout 输入矩阵 endl; // 打印输入矩阵逐行打印 for (int i 0; i matrix1.size(); i) { for (int j 0; j matrix1[0].size(); j) { cout matrix1[i][j] ; // 打印每个元素加空格分隔 } cout endl; // 每打印一行换行 } solution.setZeroes(matrix1); // 调用置零函数直接修改matrix1 cout 输出矩阵 endl; for (int i 0; i matrix1.size(); i) { for (int j 0; j matrix1[0].size(); j) { cout matrix1[i][j] ; } cout endl; } // 测试示例2 cout \n 测试示例2 endl; vectorvectorint matrix2 {{0,1,2,0},{3,4,5,2},{1,3,1,5}}; // 示例2输入 cout 输入矩阵 endl; for (int i 0; i matrix2.size(); i) { for (int j 0; j matrix2[0].size(); j) { cout matrix2[i][j] ; } cout endl; } solution.setZeroes(matrix2); cout 输出矩阵 endl; for (int i 0; i matrix2.size(); i) { for (int j 0; j matrix2[0].size(); j) { cout matrix2[i][j] ; } cout endl; } // 自定义测试自己输入一个矩阵测试算法 cout \n 自定义测试 endl; vectorvectorint matrix3 {{1,2,3,4},{5,0,7,8},{9,10,11,12},{13,14,15,0}}; cout 输入矩阵 endl; for (int i 0; i matrix3.size(); i) { for (int j 0; j matrix3[0].size(); j) { cout matrix3[i][j] ; } cout endl; } solution.setZeroes(matrix3); cout 输出矩阵 endl; for (int i 0; i matrix3.size(); i) { for (int j 0; j matrix3[0].size(); j) { cout matrix3[i][j] ; } cout endl; } return 0; // 主函数结束标志 }运行流程跟着走以示例1为例示例1输入matrix [[1,1,1],[1,0,1],[1,1,1]]m3n3复制矩阵temp [[1,1,1],[1,0,1],[1,1,1]]和原矩阵一样遍历tempi0第0行j0→1→2temp[0][j]都是1不做操作i1第1行j0temp[1][0]1j1temp[1][1]0 → 触发置零置零第1行k0→1→2matrix[1][0]0、matrix[1][1]0、matrix[1][2]0 → 此时matrix变成[[1,1,1],[0,0,0],[1,1,1]]置零第1列k0→1→2matrix[0][1]0、matrix[1][1]0、matrix[2][1]0 → 此时matrix变成[[1,0,1],[0,0,0],[1,0,1]]i2第2行j0→1→2temp[2][j]都是1不做操作遍历结束matrix就是最终结果[[1,0,1],[0,0,0],[1,0,1]]。优缺点✅ 优点逻辑最简单完全不会出错容易理解适合入门❌ 缺点空间复杂度O(mn)占用内存极大比如200x200的矩阵就要额外创建40000个元素的矩阵实际开发和面试中绝对不推荐使用。解法二优化解法O(mn) 空间面试基础写法核心思路暴力解法的问题是“复制了整个矩阵”太浪费内存。其实我们不需要复制整个矩阵只需要“标记哪些行、哪些列需要置零”就行——用两个一维数组一个存需要置零的行一个存需要置零的列。步骤1. 初始化两个标记数组row标记行col标记列→ 2. 遍历原始矩阵遇到0就标记对应行和列 → 3. 再次遍历原始矩阵根据标记数组置零。举个例子row [False, True, False] 表示“第1行需要置零”col [False, True, False] 表示“第1列需要置零”后续遍历矩阵时只要行或列被标记就把元素改成0。Python 代码逐行注释from typing import List class Solution: def setZeroes(self, matrix: List[List[int]]) - None: 函数说明原地修改矩阵不返回值 matrix: 输入的二维矩阵 # 1. 获取矩阵的行数m和列数n m len(matrix) # 行数m n len(matrix[0]) # 列数n # 2. 初始化两个标记数组默认都是False表示“不需要置零” row [False] * m # row是长度为m的数组row[i] True → 第i行需要置零 col [False] * n # col是长度为n的数组col[j] True → 第j列需要置零 # 3. 第一步遍历矩阵标记需要置零的行和列 # 外层循环遍历每一行i是行号 for i in range(m): # 内层循环遍历每一列j是列号 for j in range(n): # 如果当前元素是0说明第i行和第j列都需要置零标记对应的位置 if matrix[i][j] 0: row[i] True # 标记第i行需要置零 col[j] True # 标记第j列需要置零 # 4. 第二步根据标记数组统一置零原始矩阵 # 再次遍历矩阵的每一个元素 for i in range(m): for j in range(n): # 如果当前行被标记或者当前列被标记就把这个元素改成0 if row[i] or col[j]: matrix[i][j] 0 # 主函数测试代码和解法一一致包含示例和自定义输入 if __name__ __main__: solution Solution() # 测试示例1 print( 测试示例1 ) matrix1 [[1,1,1],[1,0,1],[1,1,1]] print(输入矩阵) for row in matrix1: print(row) solution.setZeroes(matrix1) print(输出矩阵) for row in matrix1: print(row) # 测试示例2 print(\n 测试示例2 ) matrix2 [[0,1,2,0],[3,4,5,2],[1,3,1,5]] print(输入矩阵) for row in matrix2: print(row) solution.setZeroes(matrix2) print(输出矩阵) for row in matrix2: print(row) # 自定义测试 print(\n 自定义测试 ) matrix3 [[1,2,3,4],[5,0,7,8],[9,10,11,12],[13,14,15,0]] print(输入矩阵) for row in matrix3: print(row) solution.setZeroes(matrix3) print(输出矩阵) for row in matrix3: print(row)C 代码逐行注释#include vector #include iostream using namespace std; class Solution { public: void setZeroes(vectorvectorint matrix) { // 1. 获取矩阵的行数m和列数n int m matrix.size(); int n matrix[0].size(); // 2. 初始化两个标记数组默认都是false不需要置零 vectorbool row(m, false); // row[i]为true → 第i行需要置零 vectorbool col(n, false); // col[j]为true → 第j列需要置零 // 3. 第一步遍历矩阵标记需要置零的行和列 for (int i 0; i m; i) { for (int j 0; j n; j) { if (matrix[i][j] 0) { row[i] true; // 标记第i行 col[j] true; // 标记第j列 } } } // 4. 第二步根据标记数组统一置零 for (int i 0; i m; i) { for (int j 0; j n; j) { // 行或列被标记就置零 if (row[i] || col[j]) { matrix[i][j] 0; } } } } }; // 主函数测试代码和解法一一致 int main() { Solution solution; // 测试示例1 cout 测试示例1 endl; vectorvectorint matrix1 {{1,1,1},{1,0,1},{1,1,1}}; cout 输入矩阵 endl; for (int i 0; i matrix1.size(); i) { for (int j 0; j matrix1[0].size(); j) { cout matrix1[i][j] ; } cout endl; } solution.setZeroes(matrix1); cout 输出矩阵 endl; for (int i 0; i matrix1.size(); i) { for (int j 0; j matrix1[0].size(); j) { cout matrix1[i][j] ; } cout endl; } // 测试示例2 cout \n 测试示例2 endl; vectorvectorint matrix2 {{0,1,2,0},{3,4,5,2},{1,3,1,5}}; cout 输入矩阵 endl; for (int i 0; i matrix2.size(); i) { for (int j 0; j matrix2[0].size(); j) { cout matrix2[i][j] ; } cout endl; } solution.setZeroes(matrix2); cout 输出矩阵 endl; for (int i 0; i matrix2.size(); i) { for (int j 0; j matrix2[0].size(); j) { cout matrix2[i][j] ; } cout endl; } // 自定义测试 cout \n 自定义测试 endl; vectorvectorint matrix3 {{1,2,3,4},{5,0,7,8},{9,10,11,12},{13,14,15,0}}; cout 输入矩阵 endl; for (int i 0; i matrix3.size(); i) { for (int j 0; j matrix3[0].size(); j) { cout matrix3[i][j] ; } cout endl; } solution.setZeroes(matrix3); cout 输出矩阵 endl; for (int i 0; i matrix3.size(); i) { for (int j 0; j matrix3[0].size(); j) { cout matrix3[i][j] ; } cout endl; } return 0; }运行流程以示例2为例示例2输入matrix [[0,1,2,0],[3,4,5,2],[1,3,1,5]]m3n4初始化标记数组row [False, False, False]col [False, False, False, False]第一步遍历矩阵标记行和列 标记后row [True, False, False]第0行需要置零col [True, False, False, True]第0列、第3列需要置零i0第0行j0matrix[0][0] 0 → row[0] Truecol[0] Truej1matrix[0][1] 1 → 不操作j2matrix[0][2] 2 → 不操作j3matrix[0][3] 0 → row[0] True已标记不变col[3] Truei1第1行j0→3matrix[1][j]都不是0 → 不操作i2第2行j0→3matrix[2][j]都不是0 → 不操作第二步根据标记置零i0第0行row[0] True → 所有列都置零 → 第0行变成[0,0,0,0]i1第1行row[1] False但col[0] True、col[3] True → 第0列和第3列置零 → 第1行变成[0,4,5,0]i2第2行row[2] False但col[0] True、col[3] True → 第0列和第3列置零 → 第2行变成[0,3,1,0]最终结果[[0,0,0,0],[0,4,5,0],[0,3,1,0]]和示例一致。优缺点✅ 优点空间复杂度优化到O(mn)占用内存少比如200x200矩阵只需要额外400个元素的数组逻辑简单面试中最基础、最常考的写法❌ 缺点仍需要额外创建两个数组没有达到题目“常量空间”的进阶要求。解法三最优解法O(1) 常量空间面试进阶必背核心思路进阶要求仅用常量空间不创建任何数组那我们就“利用矩阵自身的空间”——用矩阵的第一行和第一列代替解法二的两个标记数组。关键逻辑用 matrix[i][0]第i行的第一个元素标记“第i行是否需要置零”代替解法二的row数组用 matrix[0][j]第j列的第一个元素标记“第j列是否需要置零”代替解法二的col数组特殊问题matrix[0][0] 既属于第一行又属于第一列如果用它标记会分不清是“第一行需要置零”还是“第一列需要置零”所以我们用一个临时变量col0单独标记“第一列是否需要置零”matrix[0][0] 只标记“第一行是否需要置零”。步骤必记用临时变量 col0 标记第一列是否需要置零遍历矩阵从第0行第0列开始遇到0就用第一行、第一列和col0做标记从“第二行第二列”开始置零避免覆盖第一行、第一列的标记单独处理第一行根据matrix[0][0]的标记单独处理第一列根据col0的标记。Python 代码逐行注释重点讲标记和置零顺序from typing import List class Solution: def setZeroes(self, matrix: List[List[int]]) - None: 函数说明原地修改矩阵常量空间O(1)最优解 matrix: 输入的二维矩阵 # 1. 获取矩阵的行数m和列数n m len(matrix) # 行数m n len(matrix[0]) # 列数n # 2. 定义临时变量col0标记第一列是否需要置零解决matrix[0][0]的冲突 # 初始值设为11表示不需要置零0表示需要置零用1/0更方便后续判断 col0 1 # 3. 第一步遍历矩阵用第一行、第一列和col0做标记核心步骤 # 遍历所有元素i从0到m-1j从0到n-1 for i in range(m): for j in range(n): # 如果当前元素是0进行标记 if matrix[i][j] 0: # 3.1 标记第i行把第i行的第一个元素matrix[i][0]改成0 matrix[i][0] 0 # 3.2 标记第j列分两种情况避免matrix[0][0]冲突 if j 0: # j0表示当前元素在第一列用col0标记第一列需要置零 col0 0 else: # j≠0表示当前元素不在第一列用第一行的第j个元素matrix[0][j]标记 matrix[0][j] 0 # 4. 第二步置零从第二行第二列开始避免覆盖标记 # 为什么从i1、j1开始因为第一行和第一列是标记先不动它们否则标记会被覆盖 for i in range(1, m): # i从1开始跳过第一行 for j in range(1, n): # j从1开始跳过第一列 # 如果当前行的标记是0matrix[i][0] 0或者当前列的标记是0matrix[0][j] 0 if matrix[i][0] 0 or matrix[0][j] 0: matrix[i][j] 0 # 置零当前元素 # 5. 第三步单独处理第一行根据matrix[0][0]的标记 # matrix[0][0] 0 表示第一行需要置零 if matrix[0][0] 0: for j in range(n): # 遍历第一行的所有列全部置零 matrix[0][j] 0 # 6. 第四步单独处理第一列根据col0的标记 # col0 0 表示第一列需要置零 if col0 0: for i in range(m): # 遍历第一列的所有行全部置零 matrix[i][0] 0 # 主函数测试代码包含示例和自定义输入 if __name__ __main__: solution Solution() # 测试示例1 print( 测试示例1 ) matrix1 [[1,1,1],[1,0,1],[1,1,1]] # 示例1输入 print(输入矩阵) for row in matrix1: print(row) solution.setZeroes(matrix1) # 调用最优解函数 print(输出矩阵) for row in matrix1: print(row) # 预期输出[[1,0,1],[0,0,0],[1,0,1]] # 测试示例2 print(\n 测试示例2 ) matrix2 [[0,1,2,0],[3,4,5,2],[1,3,1,5]] # 示例2输入 print(输入矩阵) for row in matrix2: print(row) solution.setZeroes(matrix2) print(输出矩阵) for row in matrix2: print(row) # 预期输出[[0,0,0,0],[0,4,5,0],[0,3,1,0]] # 自定义测试 print(\n 自定义测试 ) matrix3 [[1,2,3,4],[5,0,7,8],[9,10,11,12],[13,14,15,0]] print(输入矩阵) for row in matrix3: print(row) solution.setZeroes(matrix3) print(输出矩阵) for row in matrix3: print(row) # 预期输出[[1,0,3,0],[0,0,0,0],[9,0,11,0],[0,0,0,0]]C 代码逐行注释和Python逻辑完全对应#include vector #include iostream using namespace std; class Solution { public: void setZeroes(vectorvectorint matrix) { // 1. 获取矩阵的行数m和列数n int m matrix.size(); // 行数m int n matrix[0].size(); // 列数n // 2. 临时变量col0标记第一列是否需要置零解决matrix[0][0]的冲突 // 初始值1不需要置零0需要置零 int col0 1; // 3. 第一步遍历矩阵用第一行、第一列和col0做标记核心步骤 for (int i 0; i m; i) { for (int j 0; j n; j) { if (matrix[i][j] 0) { // 3.1 标记第i行将第i行第一个元素置为0 matrix[i][0] 0; // 3.2 标记第j列分情况处理避免matrix[0][0]冲突 if (j 0) { // j0第一列用col0标记不修改matrix[0][0] col0 0; } else { // j≠0用第一行第j列元素标记第j列 matrix[0][j] 0; } } } } // 4. 第二步置零从第二行第二列开始避免覆盖标记 // 跳过第一行和第一列防止标记被破坏 for (int i 1; i m; i) { for (int j 1; j n; j) { // 只要当前行或当前列被标记对应位置为0就置零当前元素 if (matrix[i][0] 0 || matrix[0][j] 0) { matrix[i][j] 0; } } } // 5. 第三步单独处理第一行根据matrix[0][0]的标记 if (matrix[0][0] 0) { for (int j 0; j n; j) { matrix[0][j] 0; // 第一行所有元素置零 } } // 6. 第四步单独处理第一列根据col0的标记 if (col0 0) { for (int i 0; i m; i) { matrix[i][0] 0; // 第一列所有元素置零 } } } }; // 主函数测试代码和解法一、二一致便于对比 int main() { Solution solution; // 测试示例1 cout 测试示例1 endl; vectorvectorint matrix1 {{1,1,1},{1,0,1},{1,1,1}}; cout 输入矩阵 endl; for (int i 0; i matrix1.size(); i) { for (int j 0; j matrix1[0].size(); j) { cout matrix1[i][j] ; } cout endl; } solution.setZeroes(matrix1); cout 输出矩阵 endl; for (int i 0; i matrix1.size(); i) { for (int j 0; j matrix1[0].size(); j) { cout matrix1[i][j] ; } cout endl; } // 测试示例2 cout \n 测试示例2 endl; vectorvectorint matrix2 {{0,1,2,0},{3,4,5,2},{1,3,1,5}}; cout 输入矩阵 endl; for (int i 0; i matrix2.size(); i) { for (int j 0; j matrix2[0].size(); j) { cout matrix2[i][j] ; } cout endl; } solution.setZeroes(matrix2); cout 输出矩阵 endl; for (int i 0; i matrix2.size(); i) { for (int j 0; j matrix2[0].size(); j) { cout matrix2[i][j] ; } cout endl; } // 自定义测试 cout \n 自定义测试 endl; vectorvectorint matrix3 {{1,2,3,4},{5,0,7,8},{9,10,11,12},{13,14,15,0}}; cout 输入矩阵 endl; for (int i 0; i matrix3.size(); i) { for (int j 0; j matrix3[0].size(); j) { cout matrix3[i][j] ; } cout endl; } solution.setZeroes(matrix3); cout 输出矩阵 endl; for (int i 0; i matrix3.size(); i) { for (int j 0; j matrix3[0].size(); j) { cout matrix3[i][j] ; } cout endl; } return 0; }运行流程跟着走以示例1为例清晰理解标记和置零顺序示例1输入matrix [[1,1,1],[1,0,1],[1,1,1]]m3n3初始col01第一步遍历矩阵做标记核心 i0第0行j0→1→2matrix[0][j]都是1不做任何标记i1第1行 j0matrix[1][0] 1 → 不操作j1matrix[1][1] 0 → 触发标记 标记第1行matrix[1][0] 0把第1行第一个元素改成0标记第1列j≠0所以matrix[0][1] 0把第一行第1列改成0j2matrix[1][2] 1 → 不操作i2第2行j0→1→2matrix[2][j]都是1不做任何标记第二步从第二行第二列i1j1开始置零 i1第1行 j1matrix[1][0] 0行标记为0→ matrix[1][1] 0已为0不变j2matrix[1][0] 0行标记为0→ matrix[1][2] 0i2第2行 j1matrix[0][1] 0列标记为0→ matrix[2][1] 0j2matrix[2][0] 1行标记不为0、matrix[0][2] 1列标记不为0→ 不操作第三步单独处理第一行matrix[0][0] 1未标记→ 第一行不置零已符合要求第四步单独处理第一列col01未标记→ 第一列不置零已符合要求最终结果[[1,0,1],[0,0,0],[1,0,1]]和示例一致。优缺点✅ 优点空间复杂度O(1)仅用1个临时变量col0是题目要求的最优解✅ 面试高频考点必须掌握面试官常问“如何用常量空间实现”✅ 不浪费额外内存适合大规模矩阵比如1000x1000矩阵比解法一、二节省大量内存❌ 缺点逻辑比前两种解法稍复杂需要注意“标记顺序”和“单独处理第一行、第一列”容易遗漏细节比如忘记处理col0导致第一列置零错误。四、全文总结这道题的核心是“避免边遍历边置零”核心思路是“先标记后置零”三种解法从易到难适配不同场景可按以下顺序学习、掌握1. 学习顺序先学「解法一暴力解法」不用考虑优化重点理解“为什么不能边遍历边置零”掌握“复制矩阵→遍历标记→修改原矩阵”的思路打好基础再学「解法二优化解法」重点理解“标记数组”的作用学会用O(mn)空间优化掌握“标记行和列→统一置零”的核心逻辑这是面试中最基础、最常考的写法最后学「解法三最优解法」重点掌握“利用矩阵自身空间做标记”记住“col0临时变量”的作用和“先置零非首行首列→单独处理首行首列”的顺序应对面试进阶提问。2. 三种解法对比解法空间复杂度核心思路适用场景解法一暴力O(mn)复制矩阵遍历复制矩阵修改原矩阵入门、理解题意实际开发/面试不推荐解法二优化O(mn)用两个标记数组标记行和列统一置零日常开发、基础面试逻辑简单、不易出错解法三最优O(1)用矩阵首行首列col0标记单独处理首行首列面试进阶、大规模矩阵节省内存3. 易错点总结避坑坑点1边遍历边置零 → 解决办法先标记所有需要置零的行和列再统一置零坑点2解法三中忘记用col0标记第一列 → 解决办法记住matrix[0][0]不能同时标记首行和首列用col0单独标记首列坑点3解法三中置零顺序错误先置零首行首列→ 解决办法先置零“第二行第二列及以后”再单独处理首行和首列坑点4C中忘记用引用→ 解决办法函数参数必须写vectorvectorint matrix否则修改的是副本原矩阵不变坑点5Python中复制矩阵用temp matrix浅复制→ 解决办法用temp [row[:] for row in matrix]实现深复制避免修改temp影响原矩阵。