参与本项目 ,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们受益! 岛屿数量 题目链接 给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外,你可以假设该网格的四条边均被水包围。 提示: m == grid.length n == grid[i].length 1
参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们受益!
给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。

提示:
注意题目中每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
也就是说斜角度链接是不算了, 例如示例二,是三个岛屿,如图:

这道题题目是 DFS,BFS,并查集,基础题目。
本题思路,是用遇到一个没有遍历过的节点陆地,计数器就加一,然后把该节点陆地所能遍历到的陆地都标记上。
在遇到标记过的陆地节点和海洋节点的时候直接跳过。 这样计数器就是最终岛屿的数量。
那么如果把节点陆地所能遍历到的陆地都标记上呢,就可以使用 DFS,BFS或者并查集。
不少同学用广搜做这道题目的时候,超时了。 这里有一个广搜中很重要的细节:
根本原因是只要 加入队列就代表 走过,就需要标记,而不是从队列拿出来的时候再去标记走过。
很多同学可能感觉这有区别吗?
如果从队列拿出节点,再去标记这个节点走过,就会发生下图所示的结果,会导致很多节点重复加入队列。

超时写法 (从队列中取出节点再标记)
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向 void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) { queue<pair<int, int>> que; que.push({x, y}); while(!que.empty()) { pair<int ,int> cur = que.front(); que.pop(); int curx = cur.first; int cury = cur.second; visited[curx][cury] = true; // 从队列中取出在标记走过 for (int i = 0; i < 4; i++) { int nextx = curx + dir[i][0]; int nexty = cury + dir[i][1]; if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; // 越界了,直接跳过 if (!visited[nextx][nexty] && grid[nextx][nexty] == '1') { que.push({nextx, nexty}); } } } }
加入队列 就代表走过,立刻标记,正确写法:
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向 void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) { queue<pair<int, int>> que; que.push({x, y}); visited[x][y] = true; // 只要加入队列,立刻标记 while(!que.empty()) { pair<int ,int> cur = que.front(); que.pop(); int curx = cur.first; int cury = cur.second; for (int i = 0; i < 4; i++) { int nextx = curx + dir[i][0]; int nexty = cury + dir[i][1]; if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; // 越界了,直接跳过 if (!visited[nextx][nexty] && grid[nextx][nexty] == '1') { que.push({nextx, nexty}); visited[nextx][nexty] = true; // 只要加入队列立刻标记 } } } }
以上两个版本其实,其实只有细微区别,就是 visited[x][y] = true; 放在的地方,着去取决于我们对 代码中队列的定义,队列中的节点就表示已经走过的节点。 所以只要加入队列,立即标记该节点走过。
本题完整广搜代码:
class Solution { private: int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向 void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) { queue<pair<int, int>> que; que.push({x, y}); visited[x][y] = true; // 只要加入队列,立刻标记 while(!que.empty()) { pair<int ,int> cur = que.front(); que.pop(); int curx = cur.first; int cury = cur.second; for (int i = 0; i < 4; i++) { int nextx = curx + dir[i][0]; int nexty = cury + dir[i][1]; if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; // 越界了,直接跳过 if (!visited[nextx][nexty] && grid[nextx][nexty] == '1') { que.push({nextx, nexty}); visited[nextx][nexty] = true; // 只要加入队列立刻标记 } } } } public: int numIslands(vector<vector<char>>& grid) { int n = grid.size(), m = grid[0].size(); vector<vector<bool>> visited = vector<vector<bool>>(n, vector<bool>(m, false)); int result = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (!visited[i][j] && grid[i][j] == '1') { result++; // 遇到没访问过的陆地,+1 bfs(grid, visited, i, j); // 将与其链接的陆地都标记上 true } } } return result; } };
class Solution { boolean[][] visited; int[][] move = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; public int numIslands(char[][] grid) { int res = 0; visited = new boolean[grid.length][grid[0].length]; for(int i = 0; i < grid.length; i++) { for(int j = 0; j < grid[0].length; j++) { if(!visited[i][j] && grid[i][j] == '1') { bfs(grid, i, j); res++; } } } return res; } //将这片岛屿上的所有陆地都访问到 public void bfs(char[][] grid, int y, int x) { Deque<int[]> queue = new ArrayDeque<>(); queue.offer(new int[]{y, x}); visited[y][x] = true; while(!queue.isEmpty()) { int[] cur = queue.poll(); int m = cur[0]; int n = cur[1]; for(int i = 0; i < 4; i++) { int nexty = m + move[i][0]; int nextx = n + move[i][1]; if(nextx < 0 || nexty == grid.length || nexty < 0 || nextx == grid[0].length) continue; if(!visited[nexty][nextx] && grid[nexty][nextx] == '1') { queue.offer(new int[]{nexty, nextx}); visited[nexty][nextx] = true; //只要加入队列就标记为访问 } } } } }
BFS solution
class Solution: def __init__(self): self.dirs = [[0, 1], [1, 0], [-1, 0], [0, -1]] def numIslands(self, grid: List[List[str]]) -> int: m = len(grid) n = len(grid[0]) visited = [[False]*n for _ in range(m)] res = 0 for i in range(m): for j in range(n): if visited[i][j] == False and grid[i][j] == '1': res += 1 self.bfs(grid, i, j, visited) # Call bfs within this condition return res def bfs(self, grid, i, j, visited): q = deque() q.append((i,j)) visited[i][j] = True while q: x, y = q.popleft() for k in range(4): next_i = x + self.dirs[k][0] next_j = y + self.dirs[k][1] if next_i < 0 or next_i >= len(grid): continue if next_j < 0 or next_j >= len(grid[0]): continue if visited[next_i][next_j]: continue if grid[next_i][next_j] == '0': continue q.append((next_i, next_j)) visited[next_i][next_j] = True
var numIslands = function (grid) { let dir = [[0, 1], [1, 0], [-1, 0], [0, -1]]; // 四个方向 let bfs = (grid, visited, x, y) => { let queue = []; queue.push([x, y]); visited[x][y] = true; while (queue.length) { let top = queue.shift();//取出队列头部元素 console.log(top) for (let i = 0; i < 4; i++) { let nextX = top[0] + dir[i][0] let nextY = top[1] + dir[i][1] if (nextX < 0 || nextX >= grid.length || nextY < 0 || nextY >= grid[0].length) continue; if (!visited[nextX][nextY] && grid[nextX][nextY] === "1") { queue.push([nextX, nextY]) visited[nextX][nextY] = true } } } } let visited = new Array(grid.length).fill().map(() => Array(grid[0].length).fill(false)) let res = 0 for (let i = 0; i < grid.length; i++) { for (let j = 0; j < grid[i].length; j++) { if (!visited[i][j] && grid[i][j] === "1") { ++res; bfs(grid, visited, i, j); } } } return res };
function numIslands2(grid: string[][]): number { // 四个方向 const dir: number[][] = [[0, 1], [1, 0], [-1, 0], [0, -1]]; const [m, n]: [number, number] = [grid.length, grid[0].length]; function dfs(grid: string[][], visited: boolean[][], x: number, y: number) { const queue: number[][] = [[x, y]]; while (queue.length !== 0) { //取出队列头部元素 const top: number[] = queue.shift()!; for (let i = 0; i < 4; i++) { const nextX: number = top[0] + dir[i][0]; const nextY: number = top[1] + dir[i][1]; // 越界了,直接跳过 if (nextX < 0 || nextX >= m || nextY < 0 || nextY >= n) { continue; } if (!visited[nextX][nextY] && grid[nextX][nextY] === '1') { queue.push([nextX, nextY]); // 只要加入队列立刻标记 visited[nextX][nextY] = true; } } } } const visited: boolean[][] = Array.from({ length: m }, _ => new Array(n).fill(false)); let result = 0; for (let i = 0; i < m; i++) { for (let k = 0; k < n; k++) { if (!visited[i][k] && grid[i][k] === '1') { ++result; // 遇到没访问过的陆地,+1 visited[i][k] = true; dfs(grid, visited, i, k); // 将与其链接的陆地都标记上 true } } } return result; }
var DIRECTIONS = [4][2]int{{-1, 0}, {0, -1}, {1, 0}, {0, 1}} func numIslands(grid [][]byte) int { res := 0 visited := make([][]bool, len(grid)) for i := 0; i < len(grid); i++ { visited[i] = make([]bool, len(grid[0])) } for i, rows := range grid { for j, v := range rows { if v == '1' && !visited[i][j] { res++ bfs(grid, visited, i, j) } } } return res } func bfs(grid [][]byte, visited [][]bool, i, j int) { queue := [][2]int{{i, j}} visited[i][j] = true // 标记已访问,循环中标记会导致重复 for len(queue) > 0 { cur := queue[0] queue = queue[1:] for _, d := range DIRECTIONS { x, y := cur[0]+d[0], cur[1]+d[1] if x < 0 || x >= len(grid) || y < 0 || y >= len(grid[0]) { continue } if grid[x][y] == '1' && !visited[x][y] { visited[x][y] = true queue = append(queue, [2]int{x, y}) } } } }
use std::collections::VecDeque; impl Solution { const DIRECTIONS: [(i32, i32); 4] = [(0, 1), (1, 0), (-1, 0), (0, -1)]; pub fn num_islands(grid: Vec<Vec<char>>) -> i32 { let mut visited = vec![vec![false; grid[0].len()]; grid.len()]; let mut res = 0; for (i, chars) in grid.iter().enumerate() { for (j, &c) in chars.iter().enumerate() { if !visited[i][j] && c == '1' { res += 1; Self::bfs(&grid, &mut visited, (i as i32, j as i32)); } } } res } pub fn bfs(grid: &Vec<Vec<char>>, visited: &mut Vec<Vec<bool>>, (x, y): (i32, i32)) { let mut queue = VecDeque::new(); queue.push_back((x, y)); visited[x as usize][y as usize] = true; while let Some((cur_x, cur_y)) = queue.pop_front() { for (dx, dy) in Self::DIRECTIONS { let (nx, ny) = (cur_x + dx, cur_y + dy); if nx < 0 || nx >= grid.len() as i32 || ny < 0 || ny >= grid[0].len() as i32 { continue; } let (nx, ny) = (nx as usize, ny as usize); if grid[nx][ny] == '1' && !visited[nx][ny] { visited[nx][ny] = true; queue.push_back((nx as i32, ny as i32)); } } } } }
<p align="center"> <a href="https://programmercarl.com/other/kstar.html" target="_blank"> <img src="https://www.aiknowledge.cn/images/%E3%80%8A%E4%BB%A3%E7%A0%81%E9%9A%8F%E6%83%B3%E5%BD%95%E3%80%8BLeetCode+%E5%88%B7%E9%A2%98%E6%94%BB%E7%95%A5/%E7%BD%91%E7%AB%99%E6%98%9F%E7%90%83%E5%AE%A3%E4%BC%A0%E6%B5%B7%E6%8A%A5.webp" width="1000"/> </a>