题目:编写一种算法,若M * N矩阵中某个元素为0,则将其所在的行与列清零。
示例1:
输入:
[
[1,1,1],
[1,0,1],
[1,1,1]
]
输出:
[
[1,0,1],
[0,0,0],
[1,0,1]
]
示例2:
输入:
[
[0,1,2,0],
[3,4,5,2],
[1,3,1,5]
]
输出:
[
[0,0,0,0],
[0,4,5,0],
[0,3,1,0]
]
1. 思考
这题有个需要注意的点,不要将清空的元素误当作零元素,这样会导致最后整个矩阵都被清空。比如下图。
其中黄色块表示非零元素,白色块表示零元素,灰色块表示由于零元素而置空的行列中的元素。
如果按照元素顺序依次遍历判断是否需要置空所在行列的话,遍历到白色块,将所在行列元素都置空,遍历到灰色块会误认为其本来就是零元素,再次置空所在行列,这样就会造成误置空的操作。
所以需要将查询记录零元素所在行列与置空操作分开来。
可以建立两个数组,分别记录行索引或者列索引是否存在零元素,然后根据这个行列记录,将对应的行或列元素都置零。这样就避免了置零后,影响后续判断的问题。
这样空间复杂度是O(n),因为需要额外数组记录行和列是否存在零元素。
有更巧妙的方法,利用矩阵的第一行和第一列记录后续行列的零元素信息,这样空间复杂度是O(1) 。
首行记录对应列是否存在零元素,首列记录对应行是否存在零元素,所以对于首行首列的清空操作应该在最后进行。具体操作如下:
1. 判断原首行,首列是否本身存在零元素,这样避免后续在首行记录列零元素位置,或首列记录行零元素位置后无法知晓是否需要存在零元素将首行/首列清零。
2. 遍历首行/首列之外的元素,如果是零元素,首行/首列对应位置元素清零。如下图,黄色/白色块为遍历查询零元素的区域,白色块是零元素,绿色块/灰色块是用于记录行列零元素位置的区域,灰色块是遍历区域零元素的行列位置。
3. 将第一行首元素外的零元素对应的列置零。这边注意之所以跳过首元素,是为了防止[0, 0]元素为零的情况下,如果此时判断首元素,发现是零,将第一列整个置零,就丢失了零元素行位置的信息。
4. 将第一列首元素外的零元素对应的行置零。
5. 最后根据步骤1中首行首列自身是否存在零元素的信息,按需将首行/首列清零。
2. 代码实现
public void setZeroes(int[][] matrix) {
// 记录行数
int rowNum = matrix.length;
// 记录列数
int colNum = matrix[0].length;
// 记录首行是否存在零元素
boolean firstRowHasZero = false;
for (int col = 0; col < colNum; col++) {
if (matrix[0][col] == 0) {
firstRowHasZero = true;
break;
}
}
// 记录首列是否存在零元素
boolean firstColHasZero = false;
for (int row = 0; row < rowNum; row++) {
if (matrix[row][0] == 0) {
firstColHasZero = true;
break;
}
}
// 查询首列/首行之外的元素
// 如果存在零元素,将对应首行列位置置零
for (int row = 1; row < rowNum; row++) {
for (int col = 1; col < colNum; col++) {
if (matrix[row][col] == 0) {
matrix[row][0] = 0;
matrix[0][col] = 0;
}
}
}
// 判断首行首元素以外元素
// 存在零元素则将对应列置零
for (int col = 1; col < colNum; col++) {
if (matrix[0][col] == 0) {
setColZero(matrix, col);
}
}
// 判断首列首元素以外的元素
// 存在零元素则将对应行置零
for (int row = 1; row < rowNum; row++) {
if (matrix[row][0] == 0) {
setRowZero(matrix, row);
}
}
// 如果首行自身存在零元素,置零
if (firstRowHasZero) {
setRowZero(matrix, 0);
}
// 如果首列自身存在零元素,置零
if (firstColHasZero) {
setColZero(matrix, 0);
}
}
// 将row行元素置零
private void setRowZero(int[][] matrix, int row) {
int colNum = matrix[0].length;
for (int col = 0; col < colNum; col++) {
matrix[row][col] = 0;
}
}
// 将col列元素置零
private void setColZero(int[][] matrix, int col) {
int rowNum = matrix.length;
for (int row = 0; row < rowNum; row++) {
matrix[row][col] = 0;
}
}