LeetCode 岛屿数量
@author:Jingdai
@date:2020.12.29
题目描述(200题)
给你一个由
'1'
(陆地)和'0'
(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 :
输入:grid = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","1"] ] 输出:3
思路及代码
方法1 DFS和BFS
仔细阅读题目,可以发现其实题目就是求网格被 '0'
分成了几块,有几块就是有几个岛屿。如图,示例中的 '0'
将网格中的 '1'
分成了3块,结果就是3个岛屿。
根据此,我们可以对整个网格进行遍历,如果遇到 '1'
,就对它进行DFS或者BFS,将从它开始连通的 '1'
全部变成 '2'
,用 '2'
表示访问过的陆地。对网格遍历过程中,每新遇到一个 '1'
,表示一个新的岛屿,使岛屿数量加1。下面是具体代码。
DFS代码如下。
public int numIslands(char[][] grid) {
int row = grid.length;
int column = grid[0].length;
int island = 0;
for (int i = 0; i < row; i++) {
for (int j = 0; j < column; j++) {
if (grid[i][j] == '1') {
island++;
dfs(grid, i, j);
}
}
}
return island;
}
public void dfs(char[][] grid, int i, int j) {
grid[i][j] = '2';
int row = grid.length;
int column = grid[0].length;
// right i j+1
// down i+1 j
// left i j-1
// up i-1 j
if (j+1 < column && grid[i][j+1] == '1') {
dfs(grid, i, j+1);
}
if (i+1 < row && grid[i+1][j] == '1') {
dfs(grid, i+1, j);
}
if (j-1 >= 0 && grid[i][j-1] == '1') {
dfs(grid, i, j-1);
}
if (i-1 >= 0 && grid[i-1][j] == '1') {
dfs(grid, i-1, j);
}
}
BFS代码如下。
public int numIslands(char[][] grid) {
int row = grid.length;
int column = grid[0].length;
int island = 0;
for (int i = 0; i < row; i++) {
for (int j = 0; j < column; j++) {
if (grid[i][j] == '1') {
island++;
bfs(grid, i, j);
}
}
}
return island;
}
public void bfs(char[][] grid, int i, int j) {
// ** visit when enqueue
int row = grid.length;
int column = grid[0].length;
// right i j+1
// down i+1 j
// left i j-1
// up i-1 j
LinkedList<int[]> queue = new LinkedList<>();
queue.offer(new int[]{
i, j});
grid[i][j] = '2';
while (queue.size() != 0) {
int[] cur = queue.poll();
if (cur[1]+1 < column && grid[cur[0]][cur[1]+1] == '1') {
queue.offer(new int[]{
cur[0], cur[1]+1});
grid[cur[0]][cur[1]+1] = '2';
}
if (cur[0]+1 < row && grid[cur[0]+1][cur[1]] == '1') {
queue.offer(new int[]{
cur[0]+1, cur[1]});
grid[cur[0]+1][cur[1]] = '2';
}
if (cur[1]-1 >= 0 && grid[cur[0]][cur[1]-1] == '1') {
queue.offer(new int[]{
cur[0], cur[1]-1});
grid[cur[0]][cur[1]-1] = '2';
}
if (cur[0]-1 >= 0 && grid[cur[0]-1][cur[1]] == '1') {
queue.offer(new int[]{
cur[0]-1, cur[1]});
grid[cur[0]-1][cur[1]] = '2';
}
}
}
方法2 并查集
这个题目也可以利用并查集数据结构来解决。还是对整个网格进行遍历,当遇到一个 '1'
时,我们把它旁边的4个方向的点都查看一下,看是否是 '1'
,如果是 '1'
,我们就将它们 union
起来。题目中要求岛屿的数目,就是求不连通的集合有几个,我们可以记录网格总共 '1'
的个数,用 '1'
的总数减去并查集中成功 union
的次数就是最后不连通的集合的个数。
扫描二维码关注公众号,回复:
12814153 查看本文章
其实还可以优化一下, 上面我们遇到一个 '1'
时,我们会把它旁边四个方向的点都查看一下,其实不用这么多。我们其实可以仅仅查看右边和下面,因为如果上面有的话那么它一定已经在访问上面那个点时连通过了,左边同理。最后的代码如下。
// Disjoint Set
private class DisjointSet {
int[] parent;
int jointNumber = 0;
DisjointSet(int number) {
parent = new int[number];
Arrays.fill(parent, -1);
}
int find(int n) {
int p = n;
while (parent[p] >= 0) {
p = parent[p];
}
int cur = n;
int temp;
// parent[cur] > 0 for initial is -1
while (parent[cur] >= 0 && parent[cur] != p) {
temp = parent[cur];
parent[cur] = p;
cur = temp;
}
return p;
}
void union(int x, int y) {
int xParent = find(x);
int yParent = find(y);
if (xParent == yParent)
return;
parent[yParent] += parent[xParent];
parent[xParent] = yParent;
jointNumber++;
}
}
public int numIslands(char[][] grid) {
int row = grid.length;
int column = grid[0].length;
int lands = 0;
DisjointSet disjointSet = new DisjointSet(row * column);
for (int i = 0; i < row; i++) {
for (int j = 0; j < column; j++) {
if (grid[i][j] == '1') {
lands++;
// right
if (j+1 < column && grid[i][j+1] == '1') {
disjointSet.union(i*column+j, i*column+j+1);
}
// under
if (i+1 < row && grid[i+1][j] == '1') {
disjointSet.union(i*column+j, (i+1)*column+j);
}
}
}
}
return lands - disjointSet.jointNumber;
}