(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) 的额外标记数组,但本题允许,所以上述解法是最优的。
相关推荐
冻柠檬飞冰走茶1 小时前
PTA基础编程题目集 7-11 分段计算居民水费(C语言实现)
c语言·开发语言·算法
得物技术1 小时前
推荐系统体验的数字化突破:得物自动化评测平台的技术实践|AICon 文章整理
算法·架构·llm
VALENIAN瓦伦尼安教学设备2 小时前
转子/行星/平行轴齿轮箱综合故障模拟实验台可复现常见机械问题
大数据·数据库·人工智能·嵌入式硬件·算法
rannn_1112 小时前
【力扣hot100】哈希表专题——从两数之和到最长连续序列
java·算法·leetcode·哈希
小保CPP2 小时前
OpenCV C++基于极值区域滤波算法的场景文本检测(OCR)
c++·人工智能·opencv·算法·计算机视觉·ocr
来一碗刘肉面3 小时前
字符串模式匹配(朴素模式匹配算法与KMP算法)
数据结构·算法
go不是csgo3 小时前
从模板到五道 LeetCode:完全背包的 Golang 教学笔记
笔记·leetcode·golang
dyxal3 小时前
eˣ 的泰勒展开式:从“化繁为简”到“万物统一”的微积分之美
算法
BetterFlow_CFD3 小时前
COMSOL声学仿真基础知识(二)
开发语言·算法·matlab