200. 岛屿数量(Number of Islands)M
英文题目
Given an m x n 2d grid 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'
DFS
设目前指针指向一个岛屿中的某一点
(i, j)
,寻找包括此点的岛屿边界从
(i, j)
向上下左右的点进行深度搜索终止条件
- 1.
(i,j)
越过矩阵边界 - 2.
grid[i][j] == 0
,代表此分支已越过岛屿边界
- 1.
搜索岛屿的同时,执行
grid[i][j] = '0'
,把岛屿所有节点删除,以免之后重复搜索时间复杂度O(MN),其中 M 和 N 分别为行数和列数。
空间复杂度O(MN),在最坏情况下,整个网格均为陆地,深度优先搜索的深度达到 MN
# python3: 时间 108 ms, 击败 74.50%; 内存 26.6 MB, 击败 39.21% class Solution: def numIslands(self, grid: List[List[str]]) -> int: if not grid: return 0 res = 0 row, col = len(grid), len(grid[0]) for r in range(row): for c in range(col): if grid[r][c] == '1': res += 1 self.dfs(grid, r, c) return res def dfs(self, grid: List[List[str]], r: int, c: int) -> None: grid[r][c] = '0' row, col = len(grid), len(grid[0]) for x,y in [(r-1, c), (r, c-1), (r+1, c), (r, c+1)]: if 0 <= x < row and 0 <= y < col and grid[x][y] == '1': self.dfs(grid, x, y)
// c++: 时间 24 ms, 击败 98.40%; 内存 12 MB, 击败 51.31% class Solution { public: int numIslands(vector<vector<char>>& grid) { if (grid.empty()) { return 0; } int res = 0; int row = grid.size(), col = grid[0].size(); for (int r = 0; r < row; ++r) { for (int c = 0; c < col; ++c) { if (grid[r][c] == '1') { ++res; dfs(grid, r, c); } } } return res; } void dfs(vector<vector<char>>& grid, int r, int c) { grid[r][c] = '0'; int row = grid.size(), col = grid[0].size(); int directions[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}}; for (int i = 0; i < 4; ++i) { int x = r + directions[i][0]; int y = c + directions[i][1]; if (0 <= x && x < row && 0 <= y && y < col && grid[x][y] == '1') { dfs(grid, x, y); } } } };
// java: 时间 3 ms, 击败 58.83%; 内存 48.7 MB, 击败 41.14% class Solution { public int numIslands(char[][] grid) { if (grid.length == 0) { return 0; } int res = 0; int row = grid.length, col = grid[0].length; for (int r = 0; r < row; ++r) { for (int c = 0; c < col; ++c) { if (grid[r][c] == '1') { ++res; dfs(grid, r, c); } } } return res; } private void dfs(char[][] grid, int r, int c) { grid[r][c] = '0'; int row = grid.length, col = grid[0].length; int[][] directions = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}}; for (int i = 0; i < 4; ++i) { int x = r + directions[i][0]; int y = c + directions[i][1]; if (0 <= x && x < row && 0 <= y && y < col && grid[x][y] == '1') { dfs(grid, x, y); } } } }
// go: 时间 0 ms, 击败 100%; 内存 3.7 MB, 击败 35.68% func numIslands(grid [][]byte) int { if len(grid) == 0 { return 0 } res := 0 row, col := len(grid), len(grid[0]) for r := 0; r < row; r++ { for c := 0; c < col; c++ { if grid[r][c] == '1' { res++ dfs(grid, r, c) } } } return res } func dfs(grid [][]byte, r, c int) { grid[r][c] = '0' row, col := len(grid), len((grid)[0]) directions := [][]int{{-1, 0}, {0, -1}, {1, 0}, {0, 1}} for i := 0; i < 4; i++ { x := r + directions[i][0] y := c + directions[i][1] if 0 <= x && x < row && 0 <= y && y < col && grid[x][y] == '1' { dfs(grid, x, y) } } }
// javascript: 时间 76 ms, 击败 75.2%; 内存 49.1 MB, 击败 14.34% /** * @param {character[][]} grid * @return {number} */ var numIslands = function(grid) { if (!grid.length) { return 0; } let res = 0; let row = grid.length, col = grid[0].length; for (let r = 0; r < row; ++r) { for (let c = 0; c < col; ++c) { if (grid[r][c] == '1') { ++res; dfs(grid, r, c); } } } return res; }; const dfs = function(grid, r, c) { grid[r][c] = '0'; let row = grid.length, col = grid[0].length; for (let d of [[-1, 0], [0, -1], [1, 0], [0,1]]) { let x = r + d[0]; let y = c + d[1]; if (0 <= x && x < row && 0 <= y && y < col && grid[x][y] == '1') { dfs(grid, x, y); } } }
BFS
使用队列,把头部节点
(i, j)
未越界且为1的上下左右节点加入队列时间复杂度O(MN),其中 M 和 N 分别为行数和列数。
空间复杂度O(min(M,N)),在最坏情况下,整个网格均为陆地,深度优先搜索的深度达到 min(M,N)
# python3: 时间 120 ms, 击败 48.66%; 内存 26.4 MB, 击败 66.27% class Solution: def numIslands(self, grid: List[List[str]]) -> int: if not grid: return 0 res = 0 row, col = len(grid), len(grid[0]) for r in range(row): for c in range(col): if grid[r][c] == '1': self.bfs(grid, r, c) res += 1 return res # 放入队列的都是'0',且四周尚未探测的元素 def bfs(self, grid: List[List[str]], r: int, c: int) -> None: queue = deque([(r,c), ]) grid[r][c] = '0' row, col = len(grid), len(grid[0]) while queue: curX, curY = queue.popleft() for x, y in [(curX-1, curY), (curX, curY-1), (curX+1, curY), (curX, curY+1)]: if 0 <= x < row and 0 <= y < col and grid[x][y] == '1': queue.append((x, y)) grid[x][y] = '0'
// c++: 时间 32 ms, 击败 65.90%; 内存 17.31 MB, 击败 12.12% class Solution { public: int numIslands(vector<vector<char>>& grid) { if (grid.empty()) { return 0; } int res = 0; int row = grid.size(), col = grid[0].size(); for (int r = 0; r < row; ++r) { for (int c = 0; c < col; ++c) { if (grid[r][c] == '1') { ++res; bfs(grid, r, c); } } } return res; } // 放入队列中的都是'0',但其四周都尚未探测的元素 void bfs(vector<vector<char>>& grid, int r, int c) { queue<pair<int, int>> q; q.push(make_pair(r, c)); grid[r][c] = '0'; int row = grid.size(), col = grid[0].size(); int directions[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}}; while (!q.empty()) { int curX, curY; tie(curX, curY) = q.front(); q.pop(); for (int i = 0; i < 4; ++i) { int x = curX + directions[i][0]; int y = curY + directions[i][1]; if (0 <= x && x < row && 0 <= y && y < col && grid[x][y] == '1') { q.push({x, y}); grid[x][y] = '0'; } } } } };
// java: 时间 4 ms, 击败 33.27%; 内存 48.9 MB, 击败 38.97% class Solution { public int numIslands(char[][] grid) { if (grid.length == 0) { return 0; } int res = 0; int row = grid.length, col = grid[0].length; for (int r = 0; r < row; ++r) { for (int c = 0; c < col; ++c) { if (grid[r][c] == '1') { ++res; bfs(grid, r, c); } } } return res; } private void bfs(char[][] grid, int r, int c) { Queue<int[]> q = new LinkedList<>(); q.add(new int[]{r, c}); grid[r][c] = '0'; int row = grid.length, col = grid[0].length; int[][] directions = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}}; while (!q.isEmpty()) { int[] cur = q.poll(); int curX = cur[0]; int curY = cur[1]; for (int i = 0; i < 4; i++) { int x = curX + directions[i][0]; int y = curY + directions[i][1]; if (0 <= x && x < row && 0 <= y && y < col && grid[x][y] == '1') { q.add(new int[]{x, y}); grid[x][y] = '0'; } } } } }
// go: 时间 8 ms, 击败 24.81%; 内存 6.4 MB, 击败 8.99% func numIslands(grid [][]byte) int { if len(grid) == 0 { return 0 } res := 0 row, col := len(grid), len(grid[0]) for r := 0; r < row; r++ { for c := 0; c < col; c++ { if grid[r][c] == '1' { res++ bfs(grid, r, c) } } } return res } func bfs(grid [][]byte, r, c int) { q := [][]int{{r,c}} grid[r][c] = '0' row, col := len(grid), len((grid)[0]) directions := [][]int{{-1, 0}, {0, -1}, {1, 0}, {0, 1}} for len(q) > 0 { cur := q[0] q = q[1:] curX := cur[0] curY := cur[1] for i := 0; i < 4; i++ { x := curX + directions[i][0] y := curY + directions[i][1] if 0 <= x && x < row && 0 <= y && y < col && grid[x][y] == '1' { q = append(q, []int{x, y}) grid[x][y] = '0' } } } }
// javascript: 时间 80 ms, 击败 58.95%; 内存 49.3 MB, 击败 13.33% /** * @param {character[][]} grid * @return {number} */ var numIslands = function(grid) { if (!grid.length) { return 0; } let res = 0; let row = grid.length, col = grid[0].length; for (let r = 0; r < row; ++r) { for (let c = 0; c < col; ++c) { if (grid[r][c] == '1') { ++res; bfs(grid, r, c); } } } return res; }; const bfs = function(grid, r, c) { const q = [[r,c]]; grid[r][c] = '0'; let row = grid.length, col = grid[0].length; while (q.length) { const cur = q.shift(); let curX = cur[0]; let curY = cur[1]; for (let d of [[-1, 0], [0, -1], [1, 0], [0,1]]) { let x = curX + d[0]; let y = curY + d[1]; if (0 <= x && x < row && 0 <= y && y < col && grid[x][y] == '1') { q.push([x,y]); grid[x][y] = '0'; } } } }
并查集
建立并查集,遍历矩阵,然后向右向下建立并查集,中间维护并查集的数量
注意中间用了二维转一维,公式为:一维索引=当前行 x 列数 + 当前列
时间复杂度,M、N分别为行数和列数,单次操作的时间复杂度是 ,其中为反阿克曼函数,当自变量 x 的值在人类可观测的范围内(宇宙中粒子的数量)时,函数 的值不会超过 5,因此也可以看成是常数时间复杂度
空间复杂度 O(MN)
# python3: 时间 124 ms, 击败 41.96%; 内存 28.2 MB, 击败 17.32% class Solution: def numIslands(self, grid: List[List[str]]) -> int: if not grid: return 0 uf = UnionFind(grid) row, col = len(grid), len(grid[0]) for r in range(row): for c in range(col): if grid[r][c] == '0': continue # 往下和右建立并查集 for x, y in [(r+1, c), (r, c+1)]: if 0 <= x < row and 0 <= y < col and grid[x][y] == '1': uf.unite(r * col + c, x * col + y) return uf.getCount() class UnionFind: def __init__(self, grid): row, col = len(grid), len(grid[0]) self.parent = [-1] * (row * col) self.count = 0 for r in range(row): for c in range(col): if grid[r][c] == '0': continue # 初始化,把自身索引赋给自己,此时代表它是并查集的根 self.parent[r * col + c] = r * col + c 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 unite(self, i, j): rooti = self.find(i) rootj = self.find(j) if rooti == rootj: return # 此处把rooti值赋给rootj,原因是rooti更小, # 可以保持self.parent中同一并查集的都是指向同一个, # 更同一,且能减少find次数 self.parent[rootj] = self.parent[rooti] self.count -= 1 def getCount(self): return self.count
// c++: 时间 20 ms, 击败 99.79%; 内存 12.7 MB, 击败 30.16% class UnionFind { public: UnionFind(vector<vector<char>>& grid) { int row = grid.size(), col = grid[0].size(); parent = vector<int>(row * col, -1); count = 0; for (int r = 0; r < row; ++r) { for (int c = 0; c < col; ++c) { if (grid[r][c] == '0') { continue; } parent[r * col + c] = r * col + c; ++count; } } } int find(int i) { if (parent[i] != i) { parent[i] = find(parent[i]); } return parent[i]; } void unite(int i, int j) { int rooti = find(i); int rootj = find(j); if (rooti == rootj) { return; } parent[rootj] = parent[rooti]; --count; } int getCount() { return count; } private: vector<int> parent; int count; }; class Solution { public: int numIslands(vector<vector<char>>& grid) { if (grid.empty()) { return 0; } UnionFind uf(grid); int row = grid.size(), col = grid[0].size(); int directions[2][2] = {{1, 0}, {0, 1}}; for (int r = 0; r < row; ++r) { for (int c = 0; c < col; ++c) { if (grid[r][c] == '0') { continue; } for (int i = 0; i < 2; ++i) { int x = r + directions[i][0]; int y = c + directions[i][1]; if (0 <= x && x < row && 0 <= y && y < col && grid[x][y] == '1') { uf.unite(r * col + c, x * col + y); } } } } return uf.getCount(); } };
// java: 时间 6 ms, 击败 16.55%; 内存 46.4 MB, 击败 64.27% class Solution { public int numIslands(char[][] grid) { if (grid.length == 0) { return 0; } UnionFind uf = new UnionFind(grid); int row = grid.length, col = grid[0].length; int directions[][] = {{1, 0}, {0, 1}}; for (int r = 0; r < row; ++r) { for (int c = 0; c < col; ++c) { if (grid[r][c] == '0') { continue; } for (int i = 0; i < 2; ++i) { int x = r + directions[i][0]; int y = c + directions[i][1]; if (0 <= x && x < row && 0 <= y && y < col && grid[x][y] == '1') { uf.unite(r * col + c, x * col + y); } } } } return uf.getCount(); } } class UnionFind { int[] parent; int count; public UnionFind(char[][] grid) { int row = grid.length, col = grid[0].length; parent = new int[row * col]; count = 0; for (int r = 0; r < row; ++r) { for (int c = 0; c < col; ++c) { if (grid[r][c] == '0') { continue; } parent[r * col + c] = r * col + c; ++count; } } } public int find(int i) { if (parent[i] != i) { parent[i] = find(parent[i]); } return parent[i]; } public void unite(int i, int j) { int rooti = find(i); int rootj = find(j); if (rooti == rootj) { return; } parent[rootj] = parent[rooti]; --count; } public int getCount() { return count; } }
// go: 时间 8 ms, 击败 24.81%; 内存 4.7 MB, 击败 19.65% func numIslands(grid [][]byte) int { if len(grid) == 0 { return 0 } uf := NewUnionFind(grid) row, col := len(grid), len(grid[0]) directions := [][]int{{1, 0}, {0, 1}} for r := 0; r < row; r++ { for c := 0; c < col; c++ { if grid[r][c] == '0' { continue } for i := 0; i < 2; i++ { x := r + directions[i][0] y := c + directions[i][1] if 0 <= x && x < row && 0 <= y && y < col && grid[x][y] == '1' { uf.Unite(r * col + c, x * col + y) } } } } return uf.GetCount() } type UnionFind struct { Parent []int Count int } func NewUnionFind(grid [][]byte) *UnionFind { uf := &UnionFind{ Count: 0, } row, col := len(grid), len((grid)[0]) uf.Parent = make([]int, row * col) for r := 0; r < row; r++ { for c := 0; c < col; c++ { if grid[r][c] == '0' { uf.Parent[r * col + c] = -1 } else { uf.Parent[r * col + c] = r * col + c uf.Count++ } } } return uf } func (uf *UnionFind)Find(i int) int { if uf.Parent[i] != i { uf.Parent[i] = uf.Find(uf.Parent[i]) } return uf.Parent[i] } func (uf *UnionFind)Unite(i, j int) { rooti, rootj := uf.Find(i), uf.Find(j) if rooti == rootj { return } uf.Parent[rootj] = uf.Parent[rooti] uf.Count-- } func (uf *UnionFind)GetCount() int { return uf.Count }
// javascript: 时间 96 ms, 击败 20.30%; 内存 49 MB, 击败 15.23% /** * @param {character[][]} grid * @return {number} */ var numIslands = function(grid) { if (!grid.length) { return 0; } const uf = new UnionFind(grid); let row = grid.length, col = grid[0].length; for (let r = 0; r < row; ++r) { for (let c = 0; c < col; ++c) { if (grid[r][c] == '0') { continue; } for (let d of [[1, 0], [0, 1]]) { let x = r + d[0]; let y = c + d[1]; if (0 <= x && x < row && 0 <= y && y < col && grid[x][y] == '1') { uf.unite(r * col + c, x * col + y); } } } } return uf.getCount(); }; class UnionFind { constructor(grid) { let row = grid.length, col = grid[0].length; this.parent = new Array(row * col).fill(-1); this.count = 0; for (let r = 0; r < row; ++r) { for (let c = 0; c < col; ++c) { if (grid[r][c] == '0') { continue; } this.parent[r * col + c] = r * col + c; ++this.count; } } } find(i) { if (this.parent[i] != i) { this.parent[i] = this.find(this.parent[i]); } return this.parent[i]; } unite(i, j) { let rooti = this.find(i); let rootj = this.find(j); if (rooti == rootj) { return; } this.parent[rootj] = this.parent[rooti]; --this.count; } getCount() { return this.count; } }