1.3 矩阵置零【73】
1.3.1 题目描述
给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。
示例 1:
输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
输出:[[1,0,1],[0,0,0],[1,0,1]]
示例 2:
输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]
输出:[[0,0,0,0],[0,4,5,0],[0,3,1,0]]
提示:
- m == matrix.length
- n == matrix[0].length
- 1 <= m, n <= 200
- -231 <= matrix[i][j] <= 231 - 1
1.3.2 方法一:
思路和算法
我们可以用两个标记数组分别记录每一行和每一列是否有零出现。
具体地,我们首先遍历该数组一次,如果某个元素为 0,那么就将该元素所在的行和列所对应标记数组的位置置为 true。最后我们再次遍历该数组,用标记数组更新原数组即可。
代码
class Solution {
public void setZeroes(int[][] matrix) {
int m = matrix.length, n = matrix[0].length;
boolean[] row = new boolean[m];
boolean[] col = new boolean[n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == 0) {
row[i] = col[j] = true;
}
}
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (row[i] || col[j]) {
matrix[i][j] = 0;
}
}
}
}
}
复杂度分析
- 时间复杂度:O(mn),其中 m 是矩阵的行数,n 是矩阵的列数。我们至多只需要遍历该矩阵两次。
- 空间复杂度:O(m+n),其中 m 是矩阵的行数,n 是矩阵的列数。我们需要分别记录每一行或每一列是否有零出现。
1.3.3 方法二:使用两个标记变量
思路和算法
我们可以用矩阵的第一行和第一列代替方法一中的两个标记数组,以达到 O(1) 的额外空间。但这样会导致原数组的第一行和第一列被修改,无法记录它们是否原本包含 0。因此我们需要额外使用两个标记变量分别记录第一行和第一列是否原本包含 0。
在实际代码中,我们首先预处理出两个标记变量,接着使用其他行与列去处理第一行与第一列,然后反过来使用第一行与第一列去更新其他行与列,最后使用两个标记变量更新第一行与第一列即可。
class Solution {
public void setZeroes(int[][] matrix) {
int m = matrix.length, n = matrix[0].length;
boolean flagCol0 = false, flagRow0 = false;
for (int i = 0; i < m; i++) { // 记录第一列是否有0
if (matrix[i][0] == 0) {
flagCol0 = true;
}
}
for (int j = 0; j < n; j++) { // 记录第一行是否有0
if (matrix[0][j] == 0) {
flagRow0 = true;
}
}
for (int i = 1; i < m; i++) { // 扫描除了第一行第一列其他是否有0,有的话记录在第一行第一列
for (int j = 1; j < n; j++) {
if (matrix[i][j] == 0) {
matrix[i][0] = matrix[0][j] = 0;
}
}
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (matrix[i][0] == 0 || matrix[0][j] == 0) { // 通过第一行第一列是否有0将整行整列置0
matrix[i][j] = 0;
}
}
}
if (flagCol0) { // 之前的第一列有0,则将整列置0
for (int i = 0; i < m; i++) {
matrix[i][0] = 0;
}
}
if (flagRow0) { // 之前的第一行有0,则将整行置0
for (int j = 0; j < n; j++) {
matrix[0][j] = 0;
}
}
}
}
复杂度分析
- 时间复杂度:O(mn),其中 m 是矩阵的行数,n 是矩阵的列数。我们至多只需要遍历该矩阵两次。
- 空间复杂度:O(1)。我们只需要常数空间存储若干变量。
1.3.4 方法三:使用一个标记变量
思路和算法
我们可以对方法二进一步优化,只使用一个标记变量记录第一列是否原本存在 0。这样,第一列的第一个元素即可以标记第一行是否出现 0。但为了防止每一列的第一个元素被提前更新,我们需要从最后一行开始,倒序地处理矩阵元素。
代码
class Solution {
public void setZeroes(int[][] matrix) {
int m = matrix.length, n = matrix[0].length;
boolean flagCol0 = false;
for (int i = 0; i < m; i++) {
if (matrix[i][0] == 0) {
flagCol0 = true;
}
for (int j = 1; j < n; j++) {
if (matrix[i][j] == 0) {
matrix[i][0] = matrix[0][j] = 0;
}
}
}
for (int i = m - 1; i >= 0; i--) {
for (int j = 1; j < n; j++) {
if (matrix[i][0] == 0 || matrix[0][j] == 0) {
matrix[i][j] = 0;
}
}
if (flagCol0) {
matrix[i][0] = 0;
}
}
}
}
复杂度分析
- 时间复杂度:O(mn),其中 m 是矩阵的行数,n 是矩阵的列数。我们至多只需要遍历该矩阵两次。
- 空间复杂度:O(1)。我们只需要常数空间存储若干变量。
1.3.5 my answer—记录0的位置
class Solution {
public void setZeroes(int[][] matrix) {
List<coordinates> list = new ArrayList<>();
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[0].length; j++) {
if(matrix[i][j]==0){
list.add(new coordinates(i,j));
}
}
}
for (coordinates coordinates : list) {
int x = coordinates.x;
int y = coordinates.y;
for (int j = 0; j < matrix[x].length; j++) {
matrix[x][j] = 0;
}
for (int i = 0; i < matrix.length; i++) {
matrix[i][y] = 0;
}
}
}
}
class coordinates{
int x;
int y;
public coordinates(int x, int y) {
this.x = x;
this.y = y;
}
}