有一道很类似的题目:⭐算法OJ⭐并查集的应用/DFS/BFS:省份数量问题 Number of Provinces
今天这道题目DFS是最直接、最简单的解法,感兴趣的朋友可以尝试使用并查集解决。
说起群岛,不得不提起在北大西洋的腹地的法罗群岛。它们像是众神宴席上散落的碎屑,被遗忘在挪威与冰岛之间的汹涌波涛中,被风雕刻、被雾豢养。18座火山岛,340座山峰,1117公里犬牙交错的海岸线,以及比人类多1.5倍的绵羊——这里的每一寸土地都在低语:“时间在此失效。” 或许正如007电影在此取景时的台词:“人的一生应该去生活,而不仅仅是活着。” 法罗群岛,这片被《国家地理》称为“世界最美岛屿”的秘境,用狂风、绵羊和不可预测的天气,教会我们:真正的隐居,不是逃离世界,而是让世界自己找上门来——带着海鹦的鱼腥味、草皮屋顶的苔藓香,和永远湿漉漉的、倔强的诗意。
200. Number of Islands
Given an m x n
2D binary grid grid
which represents a map of '1’s (land) and '0’s (water), return the number of islands.
An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
Example 1:
Input: grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
Output: 1
Example 2:
Input: grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
Output: 3
问题描述
给定一个由 ‘1’(陆地)和 ‘0’(水)组成的二维网格,计算岛屿的数量。岛屿被水包围,并且通过水平或垂直方向上相邻的陆地连接形成。
深度优先搜索 (DFS) 解法
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
if (grid.empty() || grid[0].empty()) return 0;
int 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') {
dfs(grid, i, j);
++count;
}
}
}
return count;
}
private:
void dfs(vector<vector<char>>& grid, int i, int j) {
if (i < 0 || i >= grid.size() || j < 0 || j >= grid[0].size() || grid[i][j] != '1') {
return;
}
grid[i][j] = '0'; // 标记为已访问
// 四个方向搜索
dfs(grid, i - 1, j); // 上
dfs(grid, i + 1, j); // 下
dfs(grid, i, j - 1); // 左
dfs(grid, i, j + 1); // 右
}
};
广度优先搜索 (BFS) 解法
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
if (grid.empty() || grid[0].empty()) return 0;
int 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') {
bfs(grid, i, j);
++count;
}
}
}
return count;
}
private:
void bfs(vector<vector<char>>& grid, int i, int j) {
queue<pair<int, int>> q;
q.push({i, j});
grid[i][j] = '0'; // 标记为已访问
int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
while (!q.empty()) {
auto curr = q.front();
q.pop();
for (auto dir : dirs) {
int x = curr.first + dir[0];
int y = curr.second + dir[1];
if (x >= 0 && x < grid.size() && y >= 0 && y < grid[0].size() && grid[x][y] == '1') {
grid[x][y] = '0';
q.push({x, y});
}
}
}
}
};
并查集 (Union-Find) 解法
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) {
if (grid.empty() || grid[0].empty()) return 0;
int m = grid.size();
int n = grid[0].size();
UnionFind uf(grid);
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] == '1') {
grid[i][j] = '0';
if (i - 1 >= 0 && grid[i-1][j] == '1') {
uf.unite(i * n + j, (i-1) * n + j);
}
if (i + 1 < m && grid[i+1][j] == '1') {
uf.unite(i * n + j, (i+1) * n + j);
}
if (j - 1 >= 0 && grid[i][j-1] == '1') {
uf.unite(i * n + j, i * n + j - 1);
}
if (j + 1 < n && grid[i][j+1] == '1') {
uf.unite(i * n + j, i * n + j + 1);
}
}
}
}
return uf.getCount();
}
};
复杂度分析
DFS/BFS解法:
- 时间复杂度: O(M×N),其中 M 是行数,N 是列数
- 空间复杂度: O(M×N) 最坏情况下(全为陆地时递归深度)
并查集解法:
- 时间复杂度: O(M×N×α(M×N)),其中 α 是反阿克曼函数
- 空间复杂度: O(M×N) 用于存储父数组和秩数组
DFS和BFS解法通常更直观,而并查集解法在处理动态连接问题时更有优势。根据具体问题需求选择合适的解法。