Leecode热题100:矩阵置零(矩阵)

给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。

示例:

输入: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]

(来源:Leecode)


第一步:拆解问题的本质

我们要解决的"物理事实"是什么?

  1. 输入:一个 的数值网格。

  2. 规则:如果某个点 是 0,那么整个第 行和第 列都要变成 0。

  3. 约束:必须是原地(In-place) 。这意味着我们不能简单地通过复制一个完整的矩阵来解决问题(那会消耗 的空间)。

核心矛盾:

如果我们直接遍历并修改矩阵,我们会污染数据

  • 比如: 原本是 1,但因为它所在的第 0 行有另一个 0,它被改成了 0。

  • 当我们随后遍历到 时,我们会误以为它原本就是 0,从而错误地把第 0 列也清零。

推导出的基础真理:

我们需要一种方式来记录哪些行、哪些列需要清零,且这种记录不能干扰我们对"原始 0"的判断。


第二步:寻找存储空间的"最小化"

既然需要记录,记录在哪?

  • 方案 A(直觉推导) :创建一个 的布尔矩阵记录 0 的位置。

    • 评价:违背了原地算法的要求,空间复杂度太高。
  • 方案 B(进一步压缩) :我真的需要记录每一个点吗?不需要。我只需要知道"第 行是否要清零"和"第 列是否要清零"。

    • 物理事实 :我们需要 个标记位(行)和 个标记位(列)。

    • 空间复杂度

第一性原理的终极拷问:

如果没有额外的 空间呢?既然矩阵本身就在内存里,我们能不能借用矩阵内部的空间来做标记?


第三步:利用矩阵自身作为账本

如果我们用矩阵的第一行第一列 来充当那 的标记位:

  • 如果,我们就把 设为 0(代表第 行要清零),把 设为 0(代表第 列要清零)。

发现新的边界问题:

第一行和第一列本身也是数据。如果它们自己作为"账本",那它们原本是否包含 0 的信息就会被覆盖。

解决方案:先用两个变量记录"第一行原本是否有 0"和"第一列原本是否有 0",然后再开始借用它们。


第四步:代码实现

让我们按照上述逻辑编写 C++ 代码:

1. 初始化标记

首先,我们要确定第一行和第一列是否本来就需要清零。

cpp 复制代码
bool row0_has_zero = false;
bool col0_has_zero = false;

// 检查第一列
for (int i = 0; i < m; i++) {
    if (matrix[i][0] == 0) col0_has_zero = true;
}
// 检查第一行
for (int j = 0; j < n; j++) {
    if (matrix[0][j] == 0) row0_has_zero = true;
}

2. 遍历剩余部分并打标

我们利用第一行和第一列来记录 0 的出现情况。

cpp 复制代码
for (int i = 1; i < m; i++) {
    for (int j = 1; j < n; j++) {
        if (matrix[i][j] == 0) {
            matrix[i][0] = 0; // 标记行
            matrix[0][j] = 0; // 标记列
        }
    }
}

3. 根据标记清零

根据我们刚才在第一行/列留下的"脚印",把内部元素清零。

cpp 复制代码
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;
        }
    }
}

4. 最后处理"账本"本身

别忘了,我们第一步记录的 row0_has_zero 还没用呢。

cpp 复制代码
if (col0_has_zero) {
    for (int i = 0; i < m; i++) matrix[i][0] = 0;
}
if (row0_has_zero) {
    for (int j = 0; j < n; j++) matrix[0][j] = 0;
}

完整代码如下:

cpp 复制代码
void setZeroes(vector<vector<int>>& matrix) {
    int m = matrix.size();
    int n = matrix[0].size();
    bool row0 = false, col0 = false;

    for(int i=0; i<m; i++) if(matrix[i][0] == 0) col0 = true;
    for(int j=0; j<n; j++) if(matrix[0][j] == 0) row0 = true;

    for(int i=1; i<m; i++) {
        for(int j=1; j<n; j++) {
            if(matrix[i][j] == 0) {
                matrix[i][0] = 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) matrix[i][j] = 0;
        }
    }

    if(col0) for(int i=0; i<m; i++) matrix[i][0] = 0;
    if(row0) for(int j=0; j<n; j++) matrix[0][j] = 0;
}

通过第一性原理,我们将问题的复杂度从 降到了 的额外空间。

相关推荐
KaMeidebaby7 小时前
卡梅德生物技术快报|PD1 单克隆抗体定制配套 N 糖全谱质控开发
前端·人工智能·算法·数据挖掘·数据分析
8Qi88 小时前
LeetCode 235. 二叉搜索树的最近公共祖先(LCA)
算法·leetcode·二叉树·递归·二叉搜索树·lca·迭代
bIo7lyA8v8 小时前
算法稳定性分析中的随机扰动建模的技术8
算法
科研online9 小时前
基于多源数据和XGBoost-SHAP分析中国大陆绿地碳汇空间变异影响因素的非线性相关性与尺度差异
算法·学习方法
Cthy_hy9 小时前
拓扑排序超详解:原理 + Kahn 贪心算法
python·算法·贪心算法
三品吉他手会点灯10 小时前
C语言学习笔记 - 43.运算符与表达式 - 运算符1 - 运算符的分类和简单介绍
c语言·笔记·学习·算法
VkN2X2X4b10 小时前
算法复杂度的实验验证与误差分析的技术8
算法
其利天下技术10 小时前
风扇灯无刷电机自适应算法实战指南
算法·cocos2d·无刷电机自适应算法·bldc驱动自适应算法·其利无刷电机驱动算法
8Qi810 小时前
LeetCode 494:目标和(Target Sum)—— 题解 ✅
算法·leetcode·职场和发展·动态规划·01背包
hujinyuan2016011 小时前
2026年3月 中国电子学会青少年软件编程(Python)三级考试试卷 真题及答案
java·python·算法