(18)LeetCode 73. 矩阵置零

LeetCode 73. 矩阵置零

题目描述

给定一个 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]]

进阶:

  • 一个直接的解决方案是使用 O(mn) 的额外空间,但这并不是一个好的解法。
  • 一个简单的改进方案是使用 O(m+n) 的额外空间,但这仍然不是最优的。
  • 你能想出一个仅使用常数额外空间的解决方案吗?

解题思路

本题要求原地 修改矩阵,即不能额外申请一个同样大小的矩阵来存储结果。我们可以利用矩阵的第一行 和第一列作为标记数组,来记录哪些行和列需要被置零。

核心思想

  1. 预处理:先判断第一行和第一列本身是否含有 0,并记录下这两个布尔值(因为之后第一行和第一列会被用作标记,原始信息会被覆盖)。
  2. 扫描内部 :遍历除第一行和第一列外的所有元素,如果发现某个元素为 0,则将该元素所在行的行首 (第一列对应位置)和所在列的列顶(第一行对应位置)也置为 0。这样,第一行和第一列就充当了"标记数组"。
  3. 置零内部:再次遍历内部元素,如果它所在行的行首或所在列的列顶为 0,则将该元素置为 0。
  4. 处理第一行和第一列:根据之前记录的布尔值,决定是否将第一行或第一列全部置零。

这样做只需要两个额外的布尔变量,空间复杂度为 O(1)。


算法步骤(结合代码)

  1. 获取矩阵的行数 m 和列数 n。
  2. 用 r0 记录第一行是否原本包含 0,用 c0 记录第一列是否原本包含 0。
  3. 遍历 i 从 1 到 m-1,j 从 1 到 n-1:
    • 如果 matrix[i][j] == 0,则设置 matrix[i][0] = 0 和 matrix[0][j] = 0。
  4. 再次遍历内部元素(i>=1, j>=1):
    • 如果 matrix[i][0] == 0 或 matrix[0][j] == 0,则 matrix[i][j] = 0。
  5. 如果 c0 为真,则将第一列所有元素置零。
  6. 如果 r0 为真,则将第一行所有元素置零。

代码实现(C++)

cpp 复制代码
class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        
        // 记录第一行和第一列是否原本有 0
        bool r0 = ranges::contains(matrix[0], 0);
        bool c0 = ranges::any_of(matrix, [](auto& row){ return row[0] == 0; });
        
        // 使用第一行和第一列作为标记
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][j] == 0) {
                    matrix[i][0] = 0;   // 标记第 i 行
                    matrix[0][j] = 0;   // 标记第 j 列
                }
            }
        }
        
        // 根据标记将内部元素置零
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                    matrix[i][j] = 0;
                }
            }
        }
        
        // 如果第一列原本有 0,则整列置零
        if (c0) {
            for (auto& row : matrix) {
                row[0] = 0;
            }
        }
        
        // 如果第一行原本有 0,则整行置零
        if (r0) {
            ranges::fill(matrix[0], 0);
        }
    }
};

代码说明

  • ranges::contains(matrix[0], 0) 和 ranges::any_of 是 C++20 提供的范围算法,用于检查第一行和第一列是否包含 0。如果编译器不支持 C++20,可以用传统的 std::find 和 std::any_of 替代。
  • 标记过程只遍历内部元素(i>=1, j>=1),避免干扰第一行和第一列原本的信息(但我们已经提前备份了)。
  • 最后分别处理第一列和第一行,确保不会漏掉或错误置零。

复杂度分析

  • 时间复杂度 :O(m * n),遍历了矩阵三次(标记、置零内部、处理第一列),常数倍,因此整体为线性。
  • 空间复杂度 :O(1),只使用了两个布尔变量,符合原地算法的要求。

总结

  • 本题的关键在于用矩阵自身的第一行和第一列作为标记数组,从而避免开辟额外空间。
  • 注意要提前记录第一行和第一列是否原本有 0,否则标记过程会覆盖掉这些原始信息,导致无法正确处理第一行和第一列本身。
  • 这种"原地标记"的技巧在矩阵类问题中经常出现,值得熟练掌握。
    扩展思考 :如果要求不能修改第一行和第一列本身,则必须使用 O(m+n) 的额外标记数组,但本题允许,所以上述解法是最优的。
相关推荐
GoAI4 分钟前
DeepSeek Harness Memory (一)主流记忆插件总结
后端·算法·面试
DFT计算杂谈11 分钟前
可DIY布里渊区图像绘制编辑器
人工智能·机器学习·语言模型·矩阵·编辑器
Pniubi34 分钟前
力扣54.螺旋矩阵
算法·leetcode·矩阵
choumou_M3 小时前
算法_1:由浅入深学习快速排序QuickSort
学习·算法·排序算法
Coder_Shenshen4 小时前
罗克韦尔ACD工程归档离线变量树解析
算法·c#
心仪久了会沦陷5 小时前
C语言数据结构入门——树与二叉树详解:概念、堆实现与链式遍历
c语言·数据结构·算法
小此方5 小时前
[算法实战论:动态规划]背包问题概念,01不满背包问题,01满背包问题,背包问题的滚动数组空间优化
算法·动态规划
悠仁さん7 小时前
【C++】哈希表
c++·算法·散列表
地平线开发者7 小时前
点云算法整理与常用计算模块:从数据到模型,再到工程与量化
算法·自动驾驶
武子康9 小时前
H3-World 为何在加载 LoRA 前,先检查导入的代码?
人工智能·深度学习·算法