题目描述
给定一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。
示例 1:
输入:
11110
11010
11000
00000
输出: 1
示例 2:
输入:
11000
11000
00100
00011
输出: 3
思路实现
dfs
遍历每个点,将这个点附近所有1感染为2.统计岛的数量
public class Code_03_Islands {
public static int countIslands(int[][] m) {
if (m == null || m[0] == null) {
return 0;
}
int N = m.length;
int M = m[0].length;
int res = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (m[i][j] == 1) {
res++;
infect(m, i, j, N, M);
}
}
}
return res;
}
public static void infect(int[][] m, int i, int j, int N, int M) {
if (i < 0 || i >= N || j < 0 || j >= M || m[i][j] != 1) {
return;
}
m[i][j] = 2;
infect(m, i + 1, j, N, M);
infect(m, i - 1, j, N, M);
infect(m, i, j + 1, N, M);
infect(m, i, j - 1, N, M);
}
public static void main(String[] args) {
int[][] m1 = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 0, 1, 1, 1, 0 },
{ 0, 1, 1, 1, 0, 0, 0, 1, 0 },
{ 0, 1, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }, };
System.out.println(countIslands(m1));
int[][] m2 = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 1, 1, 1, 1, 0 },
{ 0, 1, 1, 1, 0, 0, 0, 1, 0 },
{ 0, 1, 1, 0, 0, 0, 1, 1, 0 },
{ 0, 0, 0, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }, };
System.out.println(countIslands(m2));
}
}
bfs
public static int numIslands(int[][] matrix) {
if (matrix == null || matrix[0].length == 0) return 0;
int N = matrix.length;
int M = matrix[0].length;
int nums = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (matrix[i][j] == 1) {
nums++;
matrix[i][j] = 0;
Queue<Integer> neighbor = new LinkedList<>();
neighbor.add(i * M + j);
while (!neighbor.isEmpty()) {
int id = neighbor.remove();
int row = id / M;
int col = id % M;
if (row - 1 >= 0 && matrix[row - 1][col] == 1) {
neighbor.add((row - 1) * M + col);
matrix[row - 1][col] = 0;
}
if (row + 1 < N && matrix[row + 1][col] == 1) {
neighbor.add((row + 1) * M + col);
matrix[row + 1][col] = 0;
}
if (col - 1 >= 0 && matrix[row][col - 1] == 1) {
neighbor.add(row * M + col - 1);
matrix[row][col - 1] = 0;
}
if (col + 1 < M && matrix[row][col + 1] == 1) {
neighbor.add(row * M + col + 1);
matrix[row][col + 1] = 0;
}
}
}
}
}
return nums;
}
拓展
当题目变的更为复杂,矩阵极为庞大,但有多个CPU怎么处理。
将大矩阵通过划分成多个小矩阵,通过hash分配给多个cpu。分别计算出每个矩阵的岛数量再合并。
此时会出现问题,在划分过程中会把一个连续小岛划分在两个或多个区域中。这样小矩阵在合并的时候会出现重复统计的问题?
此时使用并查集解决问题
需要记录当前小矩阵岛数量加小矩阵边界的水陆情况,在使用dfs标记的过程中,标记它是由谁触发被标记的(记录根节点)。
拿两个小岛举例:
左边小矩阵最右列和右边小矩阵的最左列是边界情况,在合并矩阵的过程中需要考虑“去重”。左边最右列的上面两个“1”是由矩阵第一个“1”触发标记的,所以记录他们的根为A。同理下面两个“1”记录根为B,右边矩阵的最左列都是由同一个“1”触发标记,所以根为C。
第一行两个1,属于不同的根A与C,说明以前没有合并过,现在进行合并,岛数量本来是左2+右2=4个,4-1=3个。
再看第二行,第二行的根也是A和C,但是第一步已经合并过了AC现在同属于一个集合。岛的数量不需要减1
第三行,一个0一个1,他们不连续,不需要考虑
第四行,两个1,分别属于B和C,需要进行合并,岛的数量减1,由3变为2.
第五行,一个0一个1,不管。
所以整个岛的数量为4-1-1 = 2。
由两个矩阵可以拓展到更多矩阵的合并,需要知道每个矩阵岛的数量,以及对每个矩阵岛的四周边界信息,就可以进行合并。