(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) 的额外标记数组,但本题允许,所以上述解法是最优的。
相关推荐
Discipline10293 小时前
尼泊尔河流流量:天气驱动的预测与高流量风险
人工智能·算法
XuCoder3 小时前
改一个数,右边全得重算,这题怎么扛住两万次查询
算法
小羊没烦恼!3 小时前
在Scrum中实施敏捷建模
java·开发语言·windows·算法·c#
weixin_307779133 小时前
基于睿擎工业开发平台的预训练视觉模型轻量化适配与低代码部署优化
开发语言·算法
hold?fish:palm4 小时前
45 二叉树的右视图
开发语言·c++·算法
粤鼎恒业4 小时前
惠州工厂电子料回收厂家推荐:从交接单反推筛选标准
大数据·数据库·算法·硬件架构·硬件工程·pcb工艺·材料工程
小爷毛毛(卓寿杰)5 小时前
【Agent 意图识别】输出协议、评估与置信度
人工智能·算法·大模型·prompt·大语言模型·agent
优质酸酸乳5 小时前
力扣100题之21合并两个有序链表
算法·leetcode·链表
旖旎夜光5 小时前
LeetCode 137 : 只出现一次的数字 II(位运算) —— 题解
c++·笔记·学习·算法·leetcode·力控
麦豆GEO5 小时前
GEO信源布局策略:看懂大模型信源偏好,搭建动态可迭代的全域信源矩阵
大数据·人工智能·矩阵