Every day a Leetcode
题目来源:200. 岛屿数量
解法1:深度优先搜索
设目前指针指向一个岛屿中的某一点 (i, j),寻找包括此点的岛屿边界。
从 (i, j) 向此点的上下左右 (i+1,j),(i-1,j),(i,j+1),(i,j-1) 做深度搜索。
终止条件:
- (i, j) 越过矩阵边界;
- grid[i][j] == 0,代表此分支已越过岛屿边界。
搜索岛屿的同时,执行 grid[i][j] = ‘0’,即将岛屿所有节点删除,以免之后重复搜索相同岛屿。
遍历整个矩阵,当遇到 grid[i][j] == ‘1’ 时,从此点开始做深度优先搜索 dfs,岛屿数 count + 1 且在深度优先搜索中删除此岛屿。
最终返回岛屿数 count 即可。
代码:
/*
* @lc app=leetcode.cn id=200 lang=cpp
*
* [200] 岛屿数量
*/
// @lc code=start
// 深度优先搜索
class Solution
{
private:
const int dx[4] = {-1, 0, 1, 0};
const int dy[4] = {0, 1, 0, -1};
public:
int numIslands(vector<vector<char>> &grid)
{
if (grid.empty())
return 0;
int m = grid.size(), n = m ? grid[0].size() : 0;
int islands = 0;
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
if (grid[i][j] == '1')
{
islands++;
dfs(grid, i, j);
}
return islands;
}
// 辅函数 - 深度优先搜索
void dfs(vector<vector<char>> &grid, int r, int c)
{
if (r < 0 || r >= grid.size() || c < 0 || c >= grid[0].size() || grid[r][c] == '0')
return;
grid[r][c] = '0';
for (int i = 0; i < 4; i++)
{
int x = dx[i], y = dy[i];
dfs(grid, r + x, c + y);
}
}
};
// @lc code=end
结果:
复杂度分析:
时间复杂度:O(m*n),其中 m 和 n 分别是二维数组 grid 的行数和列数。
空间复杂度:O(m*n),其中 m 和 n 分别是二维数组 grid 的行数和列数。在最坏情况下,整个网格均为陆地,深度优先搜索的深度达到 m*n。
解法2:广度优先搜索
同样地,我们也可以使用广度优先搜索代替深度优先搜索。
为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 1,则将其加入队列,开始进行广度优先搜索。在广度优先搜索的过程中,每个搜索到的 1 都会被重新标记为 0。直到队列为空,搜索结束。
最终岛屿的数量就是我们进行广度优先搜索的次数。
代码:
class Solution
{
public:
int numIslands(vector<vector<char>> &grid)
{
if (grid.empty())
return 0;
int m = grid.size(), n = m ? grid[0].size() : 0;
int islands = 0;
for (int r = 0; r < m; r++)
for (int c = 0; c < n; c++)
if (grid[r][c] == '1')
{
islands++;
grid[r][c] = '0';
queue<pair<int, int>> neighbors;
neighbors.push({r, c});
while (!neighbors.empty())
{
auto rc = neighbors.front();
neighbors.pop();
int row = rc.first, col = rc.second;
if (row - 1 >= 0 && grid[row - 1][col] == '1')
{
neighbors.push({row - 1, col});
grid[row - 1][col] = '0';
}
if (row + 1 < m && grid[row + 1][col] == '1')
{
neighbors.push({row + 1, col});
grid[row + 1][col] = '0';
}
if (col - 1 >= 0 && grid[row][col - 1] == '1')
{
neighbors.push({row, col - 1});
grid[row][col - 1] = '0';
}
if (col + 1 < n && grid[row][col + 1] == '1')
{
neighbors.push({row, col + 1});
grid[row][col + 1] = '0';
}
}
}
return islands;
}
};
结果:
复杂度分析:
时间复杂度:O(m*n),其中 m 和 n 分别是二维数组 grid 的行数和列数。
空间复杂度:O(min(m, n)),其中 m 和 n 分别是二维数组 grid 的行数和列数。在最坏情况下,整个网格均为陆地,队列的大小可以达到 min(m, n)。
解法3:并查集
同样地,我们也可以使用并查集代替搜索。
为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 1,则将其与相邻四个方向上的 1 在并查集中进行合并。
最终岛屿的数量就是并查集中连通分量的数目。
代码:
class UnionFind {
public:
UnionFind(vector<vector<char>>& grid) {
count = 0;
int m = grid.size();
int n = grid[0].size();
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] == '1') {
parent.push_back(i * n + j);
++count;
}
else {
parent.push_back(-1);
}
rank.push_back(0);
}
}
}
int find(int i) {
if (parent[i] != i) {
parent[i] = find(parent[i]);
}
return parent[i];
}
void unite(int x, int y) {
int rootx = find(x);
int rooty = find(y);
if (rootx != rooty) {
if (rank[rootx] < rank[rooty]) {
swap(rootx, rooty);
}
parent[rooty] = rootx;
if (rank[rootx] == rank[rooty]) rank[rootx] += 1;
--count;
}
}
int getCount() const {
return count;
}
private:
vector<int> parent;
vector<int> rank;
int count;
};
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int nr = grid.size();
if (!nr) return 0;
int nc = grid[0].size();
UnionFind uf(grid);
int num_islands = 0;
for (int r = 0; r < nr; ++r) {
for (int c = 0; c < nc; ++c) {
if (grid[r][c] == '1') {
grid[r][c] = '0';
if (r - 1 >= 0 && grid[r-1][c] == '1') uf.unite(r * nc + c, (r-1) * nc + c);
if (r + 1 < nr && grid[r+1][c] == '1') uf.unite(r * nc + c, (r+1) * nc + c);
if (c - 1 >= 0 && grid[r][c-1] == '1') uf.unite(r * nc + c, r * nc + c - 1);
if (c + 1 < nc && grid[r][c+1] == '1') uf.unite(r * nc + c, r * nc + c + 1);
}
}
}
return uf.getCount();
}
};
复杂度分析: