(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-1j 从 1 到 n-1
    • 如果 matrix[i][j] == 0,则设置 matrix[i][0] = 0matrix[0][j] = 0
  4. 再次遍历内部元素(i>=1, j>=1):
    • 如果 matrix[i][0] == 0matrix[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::findstd::any_of 替代。
  • 标记过程只遍历内部元素(i>=1, j>=1),避免干扰第一行和第一列原本的信息(但我们已经提前备份了)。
  • 最后分别处理第一列和第一行,确保不会漏掉或错误置零。

复杂度分析

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

总结

  • 本题的关键在于用矩阵自身的第一行和第一列作为标记数组,从而避免开辟额外空间。
  • 注意要提前记录第一行和第一列是否原本有 0,否则标记过程会覆盖掉这些原始信息,导致无法正确处理第一行和第一列本身。
  • 这种"原地标记"的技巧在矩阵类问题中经常出现,值得熟练掌握。
    扩展思考 :如果要求不能修改第一行和第一列本身,则必须使用 O(m+n) 的额外标记数组,但本题允许,所以上述解法是最优的。
相关推荐
2601_950760797 分钟前
TNF-α:自身免疫疾病治疗的核心靶点与信号通路解析
人工智能·算法·蛋白
不会代码的小猴21 分钟前
2. 了解Qt
开发语言·c++·笔记·qt·算法
ZC跨境爬虫21 分钟前
LeetCode 119. 杨辉三角 II(原地更新优化详解 + Java Python 实现)
java·python·leetcode
比奇堡裤头村23 分钟前
统计学习方法——支持向量机
算法·支持向量机·学习方法
程序喵大人3 小时前
【C++进阶】STL算法与函数对象 - 09 函数对象保存状态并复用规则
开发语言·c++·算法·stl·函数对象
致Great7 小时前
DeepSeek Harness插件开发实战教程:我让它自己写了一个 arXiv 搜索插件
算法
罗西的思考12 小时前
【Agent OS / AIOS】AOHP 深度解读:当 OS 开始为 Agent 而设计
人工智能·算法·机器学习
民乐团扒谱机12 小时前
【微实验】组合优化matlab实战(马科维茨投资模型):在收益与风险之间,寻找最优的人生配比
大数据·人工智能·算法·机器学习·数学建模·matlab·组合优化
Nil20813 小时前
leetcode 160相交链表
算法·leetcode·链表
迷途之人不知返14 小时前
算法系列2:滑动窗口
算法