题目描述(中等难度)

给定一个矩阵,然后找到所有含有 0 的地方,把该位置所在行所在列的元素全部变成 0。

解法一

暴力解法,用一个等大的空间把给定的矩阵存起来,然后遍历这个矩阵,遇到 0 就把原矩阵的当前行,当前列全部变作 0,然后继续遍历。

时间复杂度:O ( mn )。

空间复杂度:O(mn)。m 和 n 分别是矩阵的行数和列数。

解法二

空间复杂度可以优化一下,我们可以把哪一行有 0 ,哪一列有 0 都记录下来,然后最后统一把这些行,这些列置为 0。

  1. int row = matrix.length;
  2. int col = matrix[0].length;
  3. //用两个 bool 数组标记当前行和当前列是否需要置为 0
  4. boolean[] row_zero = new boolean[row];
  5. boolean[] col_zero = new boolean[col];
  6. for (int i = 0; i < row; i++) {
  7. for (int j = 0; j < col; j++) {
  8. //找到 0 的位置
  9. if (matrix[i][j] == 0) {
  10. row_zero[i] = true;
  11. col_zero[j] = true;
  12. }
  13. }
  14. }
  15. //将行标记为 true 的行全部置为 0
  16. for (int i = 0; i < row; i++) {
  17. if (row_zero[i]) {
  18. setRowZeroes(matrix, i);
  19. }
  20. }
  21. //将列标记为 false 的列全部置为 0
  22. for (int i = 0; i < col; i++) {
  23. if (col_zero[i]) {
  24. setColZeroes(matrix, i);
  25. }
  26. }
  27. }
  28. //第 col 列全部置为 0
  29. private void setColZeroes(int[][] matrix, int col) {
  30. for (int i = 0; i < matrix.length; i++) {
  31. matrix[i][col] = 0;
  32. }
  33. }
  34. //第 rol 行全部置为 0
  35. private void setRowZeroes(int[][] matrix, int row) {
  36. matrix[row][i] = 0;
  37. }

时间复杂度:O ( mn )。

空间复杂度:O(m + n)。m 和 n 分别是矩阵的行数和列数。

顺便说一下 说的解法,思想是一样的,只不过它没有用 bool 数组去标记,而是用两个 set 去存行和列。

这里,有一个比自己巧妙的地方时,自己比较直接的用两个函数去将行和列分别置零,但很明显自己的算法会使得一些元素重复置零。而上边提供的算法,每个元素只遍历一次就够了,很棒。

解法三

47题解法二 的思路,就是假设我们对问题足够的了解,假设存在一个数,矩阵中永远不会存在,然后我们就可以把需要变成 0 的位置先变成这个数,也就是先标记一下,最后再统一把这个数变成 0。直接贴下的代码。

  1. class Solution {
  2. public void setZeroes(int[][] matrix) {
  3. int MODIFIED = -1000000; //假设这个数字不存在于矩阵中
  4. int R = matrix.length;
  5. int C = matrix[0].length;
  6. for (int r = 0; r < R; r++) {
  7. for (int c = 0; c < C; c++) {
  8. //找到等于 0 的位置
  9. if (matrix[r][c] == 0) {
  10. // 将需要变成 0 的行和列改为之前定义的数字
  11. // 如果是 0 不要管,因为我们要找 0 的位置
  12. for (int k = 0; k < C; k++) {
  13. if (matrix[r][k] != 0) {
  14. matrix[r][k] = MODIFIED;
  15. }
  16. }
  17. for (int k = 0; k < R; k++) {
  18. if (matrix[k][c] != 0) {
  19. matrix[k][c] = MODIFIED;
  20. }
  21. }
  22. }
  23. }
  24. }
  25. for (int r = 0; r < R; r++) {
  26. for (int c = 0; c < C; c++) {
  27. // 将是定义的数字的位置变成 0
  28. if (matrix[r][c] == MODIFIED) {
  29. matrix[r][c] = 0;
  30. }
  31. }
  32. }
  33. }
  34. }

时间复杂度:O ( mn )。

空间复杂度:O(1)。m 和 n 分别是矩阵的行数和列数。

当然,这个解法局限性很强,很依赖于样例的取值,我们继续想其他的方法。

回想一下解法二,我们用了两个 bool 数组去标记当前哪些行和那些列需要置零,我们能不能在矩阵中找点儿空间去存我们的标记呢?

可以的!因为当我们找到第一个 0 的时候,这个 0 所在行和所在列就要全部更新成 0,所以它之前的数据是什么就不重要了,所以我们可以把这一行和这一列当做标记位,0 当做 false,1 当做 true,最后像解法二一样,统一更新就够了。

如上图,找到第一个 0 出现的位置,把橙色当做解法二的列标志位,黄色当做解法二的行标志位。

73. Set Matrix Zeroes - 图1

如上图,继续遍历找 0 的位置,找到后将对应的位置置为 1 即可。橙色部分的数字为 1 代表当前列要置为 0,黄色部分的数字为 1 代表当前行要置为 0。

看下代码吧。

时间复杂度:O ( mn )。

空间复杂度:O(1)。

leetcode解法三和我的思想是一样的,它标记位直接用第一行和第一列,由于第一行和第一列不一定会被置为 0,所以需要用 isCol 变量来标记第一列是否需要置为 0,用 matrix[0][0] 标记第一行是否需要置为 0。它是将用 0 表示当前行(列)需要置 0,这一点也很巧妙,相比我上边的算法就不需要初始化标记位了。

  1. class Solution {
  2. int R = matrix.length;
  3. int C = matrix[0].length;
  4. for (int i = 0; i < R; i++) {
  5. //判断第 1 列是否需要置为 0
  6. if (matrix[i][0] == 0) {
  7. isCol = true;
  8. }
  9. //找 0 的位置,将相应标记置 0
  10. for (int j = 1; j < C; j++) {
  11. if (matrix[i][j] == 0) {
  12. matrix[0][j] = 0;
  13. matrix[i][0] = 0;
  14. }
  15. }
  16. }
  17. //根据标志,将元素置 0
  18. for (int i = 1; i < R; i++) {
  19. for (int j = 1; j < C; j++) {
  20. if (matrix[i][0] == 0 || matrix[0][j] == 0) {
  21. matrix[i][j] = 0;
  22. }
  23. }
  24. }
  25. //判断第一行是否需要置 0
  26. if (matrix[0][0] == 0) {
  27. for (int j = 0; j < C; j++) {
  28. matrix[0][j] = 0;
  29. }
  30. }
  31. //判断第一列是否需要置 0
  32. if (isCol) {
  33. for (int i = 0; i < R; i++) {
  34. matrix[i][0] = 0;
  35. }
  36. }
  37. }

这道题如果对空间复杂度没有要求就很简单了,对于空间复杂度的优化,充分利用给定的空间的思想很经典了。

windliang wechat

添加好友一起进步~

如果想系统的学习数据结构和算法,强烈推荐一个我之前学过的课程,可以点击 查看详情