给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。
示例 1:
输入:
11110
11010
11000
00000
输出: 1
示例 2:
输入:
11000
11000
00100
00011
输出: 3
染色法实现:
public class 岛屿的数目 {
public static int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) {
return 0;
}
int m = grid.length;
int n = grid[0].length;
int count = 0;
//遍历所有的节点
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
//对于陆地计数+1,然后通过深度优先算法把相连的所有的陆地变成水
if (grid[i][j] != '0') {
count++;
dfs(grid, i, j, m, n);
}
}
}
return count;
}
/**
* 深度优先算法
*
* @param grid
* @param i
* @param j
* @param m
* @param n
*/
public static void dfs(char[][] grid, int i, int j, int m, int n) {
if (i < 0 || j < 0 || j >= n || i >= m) {
return;
}
if (grid[i][j] == '1') {
grid[i][j] = '0';
} else {
return;
}
//上下左右继续递归把关联的陆地变成水。这里也可以使用二维方向数组遍历前后左右
//dir[-1,1,0,0
// 0,0,1,-1] ;
dfs(grid, i + 1, j, m, n);
dfs(grid, i - 1, j, m, n);
dfs(grid, i, j - 1, m, n);
dfs(grid, i, j + 1, m, n);
}
public static void main(String[] args) {
char[][] grid = {{'1', '1', '0', '0', '0'}, {'1', '1', '0', '0', '0'}, {'0', '0', '1', '0', '0'}, {'0', '0', '0', '1', '1'}};
int result = numIslands(grid);
System.out.println(result);
}
}
并查集实现:
public class 岛屿数目并查集 {
public static int count = 0;
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) {
return 0;
}
//行
int nr = grid.length;
//列
int nc = grid[0].length;
//节点的最大的父亲
int[] parent = new int[nr * nc];
//为1的节点所在的团队的深度
int[] deep = new int[nr * nc];
//初始化各项数据
for (int i = 0; i < nr; ++i) {
for (int j = 0; j < nc; ++j) {
//首先查找所有为1的节点把他作为自己的最大父亲
if (grid[i][j] == '1') {
parent[i * nr + j] = i * nr + j;
//每个为1的节点都计数一次,后面多个节点合并的时候每合并一个节点就是-1.这样最后的count就是所有的领头人的数量
++count;
}
//记录节点所在团队的深度
deep[i * nr + j] = 0;
}
}
//遍历所有节点
for (int i = 0; i < nr; i++) {
for (int j = 0; j < nc; j++) {
//对于每个不为0的节点 依次找他的上下左右节点上级节点合并
if (grid[i][j] == '1') {
//将当前1置为0防止重复计算,也可以使用visit数组标记当前是否已读
grid[i][j] = '0';
//左
if (i - 1 > 0 && grid[i - 1][j] == '1') {
union(nr * i + j, nr * (i - 1) + j, parent, deep);
}
//右
if (i + 1 < nr && grid[i + 1][j] == '1') {
union(nr * i + j, nr * (i + 1) + j, parent, deep);
}
//上
if (j + 1 < nc && grid[i][j + 1] == '1') {
union(nr * i + j, nr * (i) + j + 1, parent, deep);
}
//下
if (j - 1 > 0 && grid[i][j - 1] == '1') {
union(nr * i + j, nr * i + j - 1, parent, deep);
}
}
}
}
return count;
}
/**
* 合并节点,为了后面的计算效率,不合并也可以,但是合并更快
* x是节点grid[i][j]在parent数组中的位置,y同理。
*/
public void union(int x, int y, int[] parent, int deep[]) {
//x找到的根节点,这个节点就是grid[i][j]所在的团队的最大的领头人
int xRoot = findParent(x, parent);
//y找到的根节点
int yRoot = findParent(y, parent);
//当x,y的最大领头人不一样的时候才表明他们不在一个团队,这时候才寻找
//为什么相等就不用找了呢?因为我们初始化的时候给每个节点作为他自己的最大领头人,如果两个节点的最大领头人一样了那肯定已经合并过了
if (xRoot != yRoot) {
//x的最大的领头人深度比y大,所以把y的最大的领头人节点指向x的最大的领头人
if (deep[xRoot] > deep[yRoot]) {
parent[yRoot] = xRoot;
} else if (deep[xRoot] < deep[yRoot]) {
parent[xRoot] = yRoot;
} else {
//深度相同随便怎么合并
parent[xRoot] = yRoot;
deep[yRoot] += 1;
}
//因为合并了此时需要减1,表示传进来的节点的上下左右节点合并了一个
--count;
}
}
/**
* 递归寻找x的最大领头人(指向自己的节点)
*
* @param x
* @param parent
* @return
*/
public int findParent(int x, int[] parent) {
if (x != parent[x]) {
parent[x] = findParent(parent[x], parent);
}
return parent[x];
}
public static void main(String[] args) {
岛屿数目并查集 c = new 岛屿数目并查集();
char[][] grid = {{'1', '1', '0', '0', '0'}, {'1', '1', '0', '0', '0'}, {'0', '0', '1', '0', '0'}, {'0', '0', '0', '1', '1'}};
int result = c.numIslands(grid);
System.out.println(result);
}
}