加载中...
200-岛屿数量(Number of Islands)
发表于:2021-12-03 | 分类: 中等
字数统计: 394 | 阅读时长: 2分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/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

 

Constraints:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 300
  • grid[i][j] is '0' or '1'.

中文题目

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

 

示例 1:

输入:grid = [
  ["1","1","1","1","0"],
  ["1","1","0","1","0"],
  ["1","1","0","0","0"],
  ["0","0","0","0","0"]
]
输出:1

示例 2:

输入:grid = [
  ["1","1","0","0","0"],
  ["1","1","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]
输出:3

 

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 300
  • grid[i][j] 的值为 '0''1'

通过代码

官方题解

📺 视频题解

LeetCode 200 ZYH.mp4

📖 文字题解

方法一:深度优先搜索

我们可以将二维网格看成一个无向图,竖直或水平相邻的 $1$ 之间有边相连。

为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 $1$,则以其为起始节点开始进行深度优先搜索。在深度优先搜索的过程中,每个搜索到的 $1$ 都会被重新标记为 $0$。

最终岛屿的数量就是我们进行深度优先搜索的次数。

下面的动画展示了整个算法。

<image.png,image.png,image.png,image.png,image.png,image.png,image.png,image.png>

[sol1-C++]
class Solution { private: void dfs(vector<vector<char>>& grid, int r, int c) { int nr = grid.size(); int nc = grid[0].size(); grid[r][c] = '0'; if (r - 1 >= 0 && grid[r-1][c] == '1') dfs(grid, r - 1, c); if (r + 1 < nr && grid[r+1][c] == '1') dfs(grid, r + 1, c); if (c - 1 >= 0 && grid[r][c-1] == '1') dfs(grid, r, c - 1); if (c + 1 < nc && grid[r][c+1] == '1') dfs(grid, r, c + 1); } public: int numIslands(vector<vector<char>>& grid) { int nr = grid.size(); if (!nr) return 0; int nc = grid[0].size(); int num_islands = 0; for (int r = 0; r < nr; ++r) { for (int c = 0; c < nc; ++c) { if (grid[r][c] == '1') { ++num_islands; dfs(grid, r, c); } } } return num_islands; } };
[sol1-Java]
class Solution { void dfs(char[][] grid, int r, int c) { int nr = grid.length; int nc = grid[0].length; if (r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == '0') { return; } grid[r][c] = '0'; dfs(grid, r - 1, c); dfs(grid, r + 1, c); dfs(grid, r, c - 1); dfs(grid, r, c + 1); } public int numIslands(char[][] grid) { if (grid == null || grid.length == 0) { return 0; } int nr = grid.length; int nc = grid[0].length; int num_islands = 0; for (int r = 0; r < nr; ++r) { for (int c = 0; c < nc; ++c) { if (grid[r][c] == '1') { ++num_islands; dfs(grid, r, c); } } } return num_islands; } }
[sol1-Python3]
class Solution: def dfs(self, grid, r, c): grid[r][c] = 0 nr, nc = len(grid), len(grid[0]) for x, y in [(r - 1, c), (r + 1, c), (r, c - 1), (r, c + 1)]: if 0 <= x < nr and 0 <= y < nc and grid[x][y] == "1": self.dfs(grid, x, y) def numIslands(self, grid: List[List[str]]) -> int: nr = len(grid) if nr == 0: return 0 nc = len(grid[0]) num_islands = 0 for r in range(nr): for c in range(nc): if grid[r][c] == "1": num_islands += 1 self.dfs(grid, r, c) return num_islands

复杂度分析

  • 时间复杂度:$O(MN)$,其中 $M$ 和 $N$ 分别为行数和列数。

  • 空间复杂度:$O(MN)$,在最坏情况下,整个网格均为陆地,深度优先搜索的深度达到 $M N$。

方法二:广度优先搜索

同样地,我们也可以使用广度优先搜索代替深度优先搜索。

为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 $1$,则将其加入队列,开始进行广度优先搜索。在广度优先搜索的过程中,每个搜索到的 $1$ 都会被重新标记为 $0$。直到队列为空,搜索结束。

最终岛屿的数量就是我们进行广度优先搜索的次数。

[sol2-C++]
class Solution { public: int numIslands(vector<vector<char>>& grid) { int nr = grid.size(); if (!nr) return 0; int nc = grid[0].size(); int num_islands = 0; for (int r = 0; r < nr; ++r) { for (int c = 0; c < nc; ++c) { if (grid[r][c] == '1') { ++num_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 < nr && 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 < nc && grid[row][col+1] == '1') { neighbors.push({row, col+1}); grid[row][col+1] = '0'; } } } } } return num_islands; } };
[sol2-Java]
class Solution { public int numIslands(char[][] grid) { if (grid == null || grid.length == 0) { return 0; } int nr = grid.length; int nc = grid[0].length; int num_islands = 0; for (int r = 0; r < nr; ++r) { for (int c = 0; c < nc; ++c) { if (grid[r][c] == '1') { ++num_islands; grid[r][c] = '0'; Queue<Integer> neighbors = new LinkedList<>(); neighbors.add(r * nc + c); while (!neighbors.isEmpty()) { int id = neighbors.remove(); int row = id / nc; int col = id % nc; if (row - 1 >= 0 && grid[row-1][col] == '1') { neighbors.add((row-1) * nc + col); grid[row-1][col] = '0'; } if (row + 1 < nr && grid[row+1][col] == '1') { neighbors.add((row+1) * nc + col); grid[row+1][col] = '0'; } if (col - 1 >= 0 && grid[row][col-1] == '1') { neighbors.add(row * nc + col-1); grid[row][col-1] = '0'; } if (col + 1 < nc && grid[row][col+1] == '1') { neighbors.add(row * nc + col+1); grid[row][col+1] = '0'; } } } } } return num_islands; } }
[sol2-Python3]
class Solution: def numIslands(self, grid: List[List[str]]) -> int: nr = len(grid) if nr == 0: return 0 nc = len(grid[0]) num_islands = 0 for r in range(nr): for c in range(nc): if grid[r][c] == "1": num_islands += 1 grid[r][c] = "0" neighbors = collections.deque([(r, c)]) while neighbors: row, col = neighbors.popleft() for x, y in [(row - 1, col), (row + 1, col), (row, col - 1), (row, col + 1)]: if 0 <= x < nr and 0 <= y < nc and grid[x][y] == "1": neighbors.append((x, y)) grid[x][y] = "0" return num_islands

复杂度分析

  • 时间复杂度:$O(MN)$,其中 $M$ 和 $N$ 分别为行数和列数。

  • 空间复杂度:$O(\min(M, N))$,在最坏情况下,整个网格均为陆地,队列的大小可以达到 $\min(M, N)$。

方法三:并查集

同样地,我们也可以使用并查集代替搜索。

为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 $1$,则将其与相邻四个方向上的 $1$ 在并查集中进行合并。

最终岛屿的数量就是并查集中连通分量的数目。

下面的动画展示了整个算法。

<image.png,image.png,image.png,image.png,image.png,image.png>

[sol3-C++]
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(); } };
[sol3-Java]
class Solution { class UnionFind { int count; int[] parent; int[] rank; public UnionFind(char[][] grid) { count = 0; int m = grid.length; int n = grid[0].length; parent = new int[m * n]; rank = new int[m * n]; for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { if (grid[i][j] == '1') { parent[i * n + j] = i * n + j; ++count; } rank[i * n + j] = 0; } } } public int find(int i) { if (parent[i] != i) parent[i] = find(parent[i]); return parent[i]; } public void union(int x, int y) { int rootx = find(x); int rooty = find(y); if (rootx != rooty) { if (rank[rootx] > rank[rooty]) { parent[rooty] = rootx; } else if (rank[rootx] < rank[rooty]) { parent[rootx] = rooty; } else { parent[rooty] = rootx; rank[rootx] += 1; } --count; } } public int getCount() { return count; } } public int numIslands(char[][] grid) { if (grid == null || grid.length == 0) { return 0; } int nr = grid.length; int nc = grid[0].length; int num_islands = 0; UnionFind uf = new UnionFind(grid); 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.union(r * nc + c, (r-1) * nc + c); } if (r + 1 < nr && grid[r+1][c] == '1') { uf.union(r * nc + c, (r+1) * nc + c); } if (c - 1 >= 0 && grid[r][c-1] == '1') { uf.union(r * nc + c, r * nc + c - 1); } if (c + 1 < nc && grid[r][c+1] == '1') { uf.union(r * nc + c, r * nc + c + 1); } } } } return uf.getCount(); } }
[sol3-Python3]
class UnionFind: def __init__(self, grid): m, n = len(grid), len(grid[0]) self.count = 0 self.parent = [-1] * (m * n) self.rank = [0] * (m * n) for i in range(m): for j in range(n): if grid[i][j] == "1": self.parent[i * n + j] = i * n + j self.count += 1 def find(self, i): if self.parent[i] != i: self.parent[i] = self.find(self.parent[i]) return self.parent[i] def union(self, x, y): rootx = self.find(x) rooty = self.find(y) if rootx != rooty: if self.rank[rootx] < self.rank[rooty]: rootx, rooty = rooty, rootx self.parent[rooty] = rootx if self.rank[rootx] == self.rank[rooty]: self.rank[rootx] += 1 self.count -= 1 def getCount(self): return self.count class Solution: def numIslands(self, grid: List[List[str]]) -> int: nr = len(grid) if nr == 0: return 0 nc = len(grid[0]) uf = UnionFind(grid) num_islands = 0 for r in range(nr): for c in range(nc): if grid[r][c] == "1": grid[r][c] = "0" for x, y in [(r - 1, c), (r + 1, c), (r, c - 1), (r, c + 1)]: if 0 <= x < nr and 0 <= y < nc and grid[x][y] == "1": uf.union(r * nc + c, x * nc + y) return uf.getCount()

复杂度分析

  • 时间复杂度:$O(MN \times \alpha(MN))$,其中 $M$ 和 $N$ 分别为行数和列数。注意当使用路径压缩(见 find 函数)和按秩合并(见数组 rank)实现并查集时,单次操作的时间复杂度为 $\alpha(MN)$,其中 $\alpha(x)$ 为反阿克曼函数,当自变量 $x$ 的值在人类可观测的范围内(宇宙中粒子的数量)时,函数 $\alpha(x)$ 的值不会超过 $5$,因此也可以看成是常数时间复杂度。

  • 空间复杂度:$O(MN)$,这是并查集需要使用的空间。

统计信息

通过次数 提交次数 AC比率
360102 640938 56.2%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言

相似题目

题目 难度
被围绕的区域 中等
墙与门 中等
岛屿数量 II 困难
无向图中连通分量的数目 中等
不同岛屿的数量 中等
岛屿的最大面积 中等
上一篇:
199-二叉树的右视图(Binary Tree Right Side View)
下一篇:
201-数字范围按位与(Bitwise AND of Numbers Range)
本文目录
本文目录