🙉饭不食,水不饮,题必须刷🙉
C语言免费动漫教程,和我一起打卡! 🌞《光天化日学C语言》🌞
LeetCode 太难?先看简单题! 🧡《C语言入门100例》🧡
数据结构难?不存在的! 🌳《数据结构入门》🌳
LeetCode 太简单?算法学起来! 🌌《夜深人静写算法》🌌
一、题目
1、题目描述
给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。两个相邻元素间的距离为 1 。
样例输入: [ 0 0 0 0 1 0 1 1 1 ] \left[ \begin{matrix} 0 & 0 & 0 \\ 0 & 1 & 0 \\ 1 & 1 & 1\end{matrix} \right] ⎣⎡001011001⎦⎤
样例输出: [ 0 0 0 0 1 0 1 2 1 ] \left[ \begin{matrix} 0 & 0 & 0 \\ 0 & 1 & 0 \\ 1 & 2 & 1\end{matrix} \right] ⎣⎡001012001⎦⎤
2、基础框架
- c++ 版本给出的基础框架代码如下:
class Solution {
public:
vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
}
};
vector<vector<int>>& mat
代表的是一个二维数组,用来代表题意所要求的矩阵,&
作为引用,用来加速参数传递。- 返回值是一个
vector<vector<int>>
,代表返回也是一个二维矩阵。可以在函数内部动议一个返回值vector<vector<int>> ret
,然后再去填充这个ret
。因为这块内存是在堆上申请的,返回时不会被销毁。
3、原题链接
二、解题报告
1、思路分析
寻找每个非 0 元素最近的 0 的过程,可以采用广度优先搜索,因为广搜找的是最短路,找到的第一个 0 一定是最近的。
如果一个矩阵右下角是 0,其余全是 1,那么左上角的 1 寻找 0 的时间复杂度就是 O ( n m ) O(nm) O(nm),最坏情况下 n × m n \times m n×m 个元素,所以最坏时间复杂度为 O ( n 2 m 2 ) O(n^2m^2) O(n2m2)。一共最多有 100000 个元素,所以肯定是无法接受的。
例如,图中黄色块代表非0,红色块代表 0,想要枚举每个点进行广搜,枚举过程 O ( n m ) O(nm) O(nm),广搜过程 O ( n m ) O(nm) O(nm),所以总的时间复杂度为 O ( n 2 m 2 ) O(n^2m^2) O(n2m2)。
所以我们可以考虑反着来,将所有的 0 哈希后压入队列,然后从 0 开始搜,遇到哈希过的就不重复搜索了,没有哈希过的,哈希完继续压入队列,同时记录步数,这样,当队列为空的时候,每个矩阵元素都被访问完毕了,而且都只会被访问一次。
- 广搜的更多内容,可以参考这篇文章:夜深人静写算法(十)- 单向广搜。
2、时间复杂度
- 对于一个 n × m n \times m n×m 的矩阵,每个元素只会访问一次,时间复杂度为 O ( n m ) O(nm) O(nm)。
3、代码详解
int dir[4][2] = {
{0, 1}, // right
{1, 0}, // down
{0, -1}, // left
{-1, 0}, // up
};
const int maxn = 10010;
class Solution {
int n, m;
int visited[maxn];
queue <int> que;
int getVisitedId(int x, int y) { // (1)
return x * m + y;
}
void getPosByVisitedId(int visitedId, int &x, int &y) { // (2)
x = visitedId / m;
y = visitedId % m;
}
void init(vector<vector<int>>& mat) {
while(!que.empty()) { // (3)
que.pop();
}
memset(visited, -1, sizeof(visited)); // (4)
n = mat.size(); // (5)
m = mat[0].size(); // (6)
for(int i = 0; i < n; ++i) {
for(int j = 0; j < m; ++j) {
if(!mat[i][j]) { // (7)
visited[ getVisitedId(i, j) ] = 0;
que.push( getVisitedId(i, j) );
}
}
}
}
bool outOfBound(int x, int y) {
return x < 0 || x >= n || y < 0 || y >= m;
}
void bfs(vector<vector<int>>& mat) {
while(!que.empty()) {
int vid = que.front(); // (8)
int x, y;
getPosByVisitedId(vid, x, y); // (9)
que.pop();
for(int i = 0; i < 4; ++i) { // (10)
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if(outOfBound(tx, ty)) {
continue;
}
int nextvid = getVisitedId(tx, ty); // (11)
if(visited[nextvid] == -1) {
visited[nextvid] = visited[vid] + 1;
que.push(nextvid);
}
}
}
}
void output(int *visited, vector<vector<int>>& ret) { // (12)
for(int i = 0; i < n; ++i) {
vector <int> ans;
for(int j = 0; j < m; ++j) {
ans.push_back( visited[ getVisitedId(i, j) ]);
}
ret.push_back(ans);
}
}
public:
vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
vector<vector<int>> ret;
init(mat);
bfs(mat);
output(visited, ret);
return ret;
}
};
- ( 1 ) (1) (1) 将二维向量映射到一维,方便索引;
- ( 2 ) (2) (2) 将一维向量拆解成二维,方便计算;
- ( 3 ) (3) (3) 定义的队列为类的私有成员,所以每次计算,初始化的时候需要首先进行清空;
-
(
4
)
(4)
(4) 利用
memset
初始化将所有矩阵元素的标记位全部置为-1
,关于memset
更多用法,可以参考:《C/C++ 面试 100 例》(六)memset 全网最全总结; - ( 5 ) (5) (5) 矩阵的行,保存成类的成员变量,方便成员函数使用;
- ( 6 ) (6) (6) 矩阵的列,保存成类的成员变量,方便成员函数使用;
- ( 7 ) (7) (7) 找到矩阵中所有的 0,将标记为置为 0,代表最近的零的距离为 0,然后塞入队列;
- ( 8 ) (8) (8) 每次从队列头部弹出一个元素;
- ( 9 ) (9) (9) 将它转换成坐标的形式,方便进行上下左右的运算;
- ( 10 ) (10) (10) 枚举四个方向扩散;
- ( 11 ) (11) (11) 得到一个相邻位置,如果这个位置没有被访问过,则将步数置为 当前位置步数 + 1;
-
(
12
)
(12)
(12)
output
函数用于将搜索结果存储到ret
中用于返回用;
三、本题小知识
1)一维的
vector
可以当数组用,二维的vector
可以当矩阵用。
2)最短路的逆向思维,正着走费劲的时候我们就反着走。
3)利用memset
可以将标记置为-1
代表尚未访问。
四、加群须知
相信看我文章的大多数都是「 大学生 」,能上大学的都是「 精英 」,那么我们自然要「 精益求精 」,如果你还是「 大一 」,那么太好了,你拥有大把时间,当然你可以选择「 刷剧 」,然而,「 学好算法 」,三年后的你自然「 不能同日而语 」。
那么这里,我整理了「 几十个基础算法 」 的分类,点击开启:
如果链接被屏蔽,或者有权限问题,可以私聊作者解决。
大致题集一览:
为了让这件事情变得有趣,以及「 照顾初学者 」,目前题目只开放最简单的算法 「 枚举系列 」 (包括:线性枚举、双指针、前缀和、二分枚举、三分枚举),当有 一半成员刷完 「 枚举系列 」 的所有题以后,会开放下个章节,等这套题全部刷完,你还在群里,那么你就会成为「 夜深人静写算法 」专家团 的一员。
不要小看这个专家团,三年之后,你将会是别人 望尘莫及 的存在。如果要加入,可以联系我,考虑到大家都是学生, 没有「 主要经济来源 」,在你成为神的路上,「 不会索取任何 」。
🔥联系作者,或者扫作者主页二维码加群,加入刷题行列吧🔥
🔥让天下没有难学的算法🔥
C语言免费动漫教程,和我一起打卡! 🌞《光天化日学C语言》🌞
入门级C语言真题汇总 🧡《C语言入门100例》🧡
几张动图学会一种数据结构 🌳《画解数据结构》🌳
组团学习,抱团生长 🌌《算法入门指引》🌌
竞赛选手金典图文教程 💜《夜深人静写算法》💜