news 2026/4/17 19:59:19

LeetCode HOT 100 —— 矩阵置零(多种解法详解)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LeetCode HOT 100 —— 矩阵置零(多种解法详解)

题目描述

LeetCode 73. 矩阵置零

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

示例 1:

text

输入:matrix = [[1,1,1],[1,0,1],[1,1,1]] 输出:[[1,0,1],[0,0,0],[1,0,1]]

示例 2:

text

输入: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) 的额外空间,但这仍然不是最好的解决方案。

  • 你能想出一个仅使用常量空间的解决方案吗?


解题思路

这道题最大的坑点在于:如果在遍历过程中直接修改矩阵,新赋值的0会影响后续判断,导致整个矩阵全变成0。因此,我们需要采取"先标记,后修改"的策略。

下面介绍三种解法,从最容易想到到最节省空间,逐步优化。


解法一:标记数组(O(m+n) 空间)

思路

这是最直观的解法。我们创建两个布尔数组:rowcol,分别记录每一行和每一列是否需要被置零。

  1. 第一次遍历矩阵,如果matrix[i][j] == 0,则将row[i]col[j]都标记为true

  2. 第二次遍历矩阵,如果row[i]col[j]true,则将当前位置置为 0。

代码实现

java

class Solution { public void setZeroes(int[][] matrix) { int m = matrix.length, n = matrix[0].length; boolean[] row = new boolean[m]; boolean[] col = new boolean[n]; // 第一次遍历:记录哪些行和列需要置零 for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (matrix[i][j] == 0) { row[i] = true; col[j] = true; } } } // 第二次遍历:根据标记置零 for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (row[i] || col[j]) { matrix[i][j] = 0; } } } } }

复杂度分析

  • 时间复杂度:O(m × n),需要遍历两次矩阵。

  • 空间复杂度:O(m + n),需要两个额外数组。


解法二:两个标记变量(O(1) 空间,优化版)

思路

能不能不使用额外数组?我们可以利用矩阵的第一行和第一列来存储标记信息。

具体做法:

  1. 用两个布尔变量rowZerocolZero记录第一行和第一列本身是否原本包含 0。

  2. (1,1)位置开始遍历,如果matrix[i][j] == 0,则将matrix[i][0]matrix[0][j]标记为 0。

  3. 第二次遍历(从(1,1)开始),如果matrix[i][0] == 0matrix[0][j] == 0,则将当前位置置为 0。

  4. 最后根据rowZerocolZero决定是否将第一行和第一列置为 0。

为什么要单独记录第一行和第一列?

因为第一行和第一列同时承担了"存储标记"的角色,它们原本的状态会被覆盖。如果不提前记录,我们就不知道第一行和第一列本身是否需要被置零。

代码实现

java

class Solution { public void setZeroes(int[][] matrix) { int m = matrix.length, n = matrix[0].length; boolean rowZero = false, colZero = false; // 1. 记录第一行和第一列是否原本包含 0 for (int i = 0; i < m; i++) { if (matrix[i][0] == 0) colZero = true; } for (int j = 0; j < n; j++) { if (matrix[0][j] == 0) rowZero = true; } // 2. 使用第一行和第一列作为标记 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. 根据标记置零(从 (1,1) 开始) 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. 最后处理第一行和第一列 if (rowZero) { for (int j = 0; j < n; j++) matrix[0][j] = 0; } if (colZero) { for (int i = 0; i < m; i++) matrix[i][0] = 0; } } }

复杂度分析

  • 时间复杂度:O(m × n)

  • 空间复杂度:O(1)


解法三:一个标记变量(极简版)

思路

能否进一步优化,只用一个标记变量?答案是肯定的。

我们只用colZero记录第一列是否包含 0,而第一行是否包含 0 的信息可以存储在matrix[0][0]中。

但这里有一个关键细节:置零操作需要从最后一行开始倒序进行。因为matrix[0][0]既代表第一行的状态,也代表第一列的状态,如果正序处理,第一行的状态可能会被提前覆盖。

代码实现

java

class Solution { public void setZeroes(int[][] matrix) { int m = matrix.length, n = matrix[0].length; boolean colZero = false; for (int i = 0; i < m; i++) { // 检查第一列是否有 0 if (matrix[i][0] == 0) colZero = true; // 检查其他列,并将标记存储在第一行和第一列 for (int j = 1; j < n; j++) { if (matrix[i][j] == 0) { matrix[i][0] = 0; matrix[0][j] = 0; } } } // 倒序遍历,避免标记被覆盖 for (int i = m - 1; i >= 0; i--) { for (int j = 1; j < n; j++) { if (matrix[i][0] == 0 || matrix[0][j] == 0) { matrix[i][j] = 0; } } if (colZero) { matrix[i][0] = 0; } } } }

复杂度分析

  • 时间复杂度:O(m × n)

  • 空间复杂度:O(1)


三种解法对比

解法空间复杂度优点缺点
标记数组O(m+n)简单直观,不易出错需要额外空间
两个标记变量O(1)空间最优,思路清晰需要单独记录第一行/列
一个标记变量O(1)最节省空间倒序遍历容易写错,可读性略差

推荐:面试时建议先给出解法一,然后主动优化到解法二,这样能展示你的优化思维。解法三可以作为扩展了解,实际工程中更推荐解法二,因为可读性更好。


踩坑提醒

  1. 不要边遍历边置零:这是最常见的错误。如果遇到 0 就立即将整行整列置零,后面遍历到的 0 可能是刚被赋值的,会导致错误地多置零。

  2. 注意第一行和第一列的特殊性:当使用原地标记法时,一定要先记录第一行和第一列的原始状态,否则它们的状态会丢失。

  3. 考虑边界情况:如果矩阵只有一行或一列,确保代码仍然能正确运行。


总结

这道题的核心思想是"标记后置"—— 先记录哪些行/列需要置零,再进行修改。从 O(mn) 到 O(m+n) 再到 O(1) 的空间优化过程,是一道非常经典的考察原地算法的题目。

掌握这三种解法,不仅能帮你通过这道题,更能让你对矩阵操作类的题目有更深的理解。


题目链接 & 题解链接

  • 题目链接:LeetCode 73. 矩阵置零

  • 本题题解链接:LeetCode 官方题解 - 矩阵置零


如果这篇文章对你有帮助,欢迎点赞、收藏、关注!更多 LeetCode HOT 100 题解持续更新中~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 19:57:16

100个小工具挑战 #002 | 做了个能直接编辑树形视图的 JSON 格式化工具

起因 今年给自己定了个目标&#xff1a;做 100 个小工具页面。 不是为了流量&#xff0c;就是想把平时开发中遇到的痛点一个个解决掉。这是第 2 个。 第 1 个是发票批量识别工具&#xff0c;这次做的是 JSON 格式化。 为什么要自己做&#xff0c;不用现成的&#xff1f; 用…

作者头像 李华
网站建设 2026/4/17 19:57:12

指纹浏览器与代理IP协同优化:网络层与设备层的深度适配实践

在 2026 年平台 AI 风控持续升级的背景下&#xff0c;多账号安全运营已不再是单一工具的独立作用&#xff0c;而是指纹浏览器与代理 IP 的协同作战。多数运营者在实际使用中&#xff0c;常陷入 “指纹隔离到位但 IP 配置不当”“IP 质量优质但环境参数冲突” 的困境&#xff0c…

作者头像 李华
网站建设 2026/4/17 19:56:27

打卡信奥刷题(3126)用C++实现信奥题 P7428 [THUPC 2017] 母亲节的礼物

P7428 [THUPC 2017] 母亲节的礼物 题目描述 小 B 喜欢图&#xff0c;尤其是边数不太多的无向简单图。 母亲节快到了&#xff0c;小 B 在纸上画了一张有 nnn 个节点、mmm 条边的无向简单图&#xff08;即&#xff0c;不存在重边、自环&#xff09;&#xff0c;保证每个点只和最多…

作者头像 李华
网站建设 2026/4/17 19:52:13

微服务4:Spring Cloud 微服务实战:如何实现跨服务数据组装?

在微服务架构中&#xff0c;数据孤岛是一个常见的问题。当我们进行业务开发时&#xff0c;经常会遇到这样的场景&#xff1a;一个业务实体&#xff08;如订单&#xff09;需要展示另一个业务实体&#xff08;如用户&#xff09;的信息。今天&#xff0c;我们就通过一个经典的“…

作者头像 李华
网站建设 2026/4/17 19:51:09

清迈自由行找导游?这些避坑经验帮你少花冤枉钱

在现代社会&#xff0c;我们每天都承受着巨大的压力&#xff0c;焦虑如影随形。旅行&#xff0c;便成了我们逃离现实、放松身心的最佳方式。我一直向往清迈这座充满异域风情的小城&#xff0c;那里有古朴的寺庙、热闹的夜市&#xff0c;还有热情友善的当地人。于是&#xff0c;…

作者头像 李华