[分析]
BFS & DFS法详见实现。
这里阐述下union-find思路,思路很直接,我们需要把相连在一起的1union起来,最后数下union了多少个集合1。输入时一个m*n矩阵,union-find相应地需要一个二维数组保存信息,采用按秩求并方法,初始时每个1元素的秩为-1,0元素不参与union,记为0。扫描数组,对于(i,j)元素,查看其是否需要同右边和下边相邻元素合并,其上边和左边不需要,因为按这种扫描顺序已经查看过。一个相邻的1集合合并完,只有根节点的秩为负数,而其余节点均指向相应根节点的下标为正数,因此扫描完整个集合,通过检查秩数组中负数的个数即可知道grid中的岛屿数。特别注意,在union时若两个元素根相同说明已经被union过,不能再执行union逻辑否则会导致该集合的根消失而Wrong Answer。
--第二版union find,一维数组存储,更清晰,使用count计数岛屿数,初始时每个1所在位置为一个岛屿,伴随union过程,相连的1代表的岛屿进行合并,每一次成功合并,岛屿数减一。实现如Method 4所示。当然此题用BFS或者DFS代码比较简洁, DFS的隐患是可能堆栈溢出。
public int numIslands1(char[][] grid) {
if (grid == null || grid.length == 0 || grid[0].length == 0)
return 0;
initUnionFind(grid);
int rows = grid.length;
int cols = grid[0].length;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (grid[i][j] == '1') {
for (int k = 0; k < 4; k++) {
int iNeigh = i + deltaY[k];
int jNeigh = j + deltaX[k];
if (iNeigh >= 0 && iNeigh < rows && jNeigh >= 0 && jNeigh < cols && grid[iNeigh][jNeigh] == '1') {
union(i * cols + j, iNeigh * cols + jNeigh);
}
}
}
}
}
return count;
}
int[] s;
int[] rank;
int count = 0;
int[] deltaX = {0, 0, 1, -1};
int[] deltaY = {1, -1, 0, 0};
private void union(int p, int q) {
int pRoot = find(p), qRoot = find(q);
if (pRoot == qRoot) return;
if (s[pRoot] > s[qRoot]) {
s[pRoot] = qRoot;
} else {
if (s[pRoot] == s[qRoot])
s[pRoot]--;
s[qRoot] = pRoot;
}
count--;
}
private int find1(int p) {
if (s[p] == p)
return p;
else
return s[p] = find(s[p]);
}
private void union1(int p, int q) {
int pRoot = find(p), qRoot = find(q);
if (pRoot == qRoot) return;
if (rank[pRoot] < rank[qRoot]) {
s[pRoot] = qRoot;
} else {
if (rank[pRoot] == rank[qRoot])
rank[pRoot]++;
s[qRoot] = s[pRoot];
}
count--;
}
// Method 3:Union-Find
public class Solution {
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0 || grid[0].length == 0)
return 0;
initUnionFind(grid);
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '0') continue;
if (j + 1 < n && grid[i][j + 1] == '1')
union(i, j, i, j + 1);
if (i + 1 < m && grid[i + 1][j] == '1')
union(i, j, i + 1, j);
}
}
int count = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (s[i][j] < 0)
count++;
}
}
return count;
}
int[][] s;
int m, n;
public void initUnionFind(char[][] grid) {
m = grid.length;
n = grid[0].length;
s = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '1')
s[i][j] = -1;
}
}
}
public int find(int i, int j) {
if (s[i][j] < 0) {
return i * n + j;
} else {
return s[i][j] = find(s[i][j] / n, s[i][j] % n);
}
}
public void union(int i1, int j1, int i2, int j2) {
int root1 = find(i1, j1), root2 = find(i2, j2);
if (root1 == root2) return; // without this, fail @["111","111","111"]
int iRoot1 = root1 / n, jRoot1 = root1 % n;
int iRoot2 = root2 / n, jRoot2 = root2 % n;
if (s[iRoot1][jRoot1] < s[iRoot2][jRoot2]) {
s[iRoot2][jRoot2] = iRoot1 * n + jRoot1;
} else {
if (s[iRoot1][jRoot1] == s[iRoot2][jRoot2])
s[iRoot2][jRoot2]--;
s[iRoot1][jRoot1] = iRoot2 * n + jRoot2;
}
}
}
// Method 2: DFS
public class Solution {
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0 || grid[0].length == 0)
return 0;
int m = grid.length, n = grid[0].length;
int count = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '1') {
count++;
dfs(i, j, grid);
}
}
}
return count;
}
public void dfs(int i, int j, char[][] grid) {
if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] == '0')
return;
grid[i][j] = '0';
dfs(i - 1, j, grid);
dfs(i + 1, j, grid);
dfs(i, j - 1, grid);
dfs(i, j + 1, grid);
}
}
// Method 1:BFS
public class Solution {
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0 || grid[0].length == 0)
return 0;
int rows = grid.length, cols = grid[0].length;
int num = 0;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (grid[i][j] == '1') {
bfs(i, j, grid);
num++;
}
}
}
return num;
}
private void bfs(int i, int j, char[][] grid) {
grid[i][j] = '2';
LinkedList<Integer> queue = new LinkedList<Integer>();
int cols = grid[0].length;
queue.offer(i * cols + j);
while (!queue.isEmpty()) {
int idx = queue.poll();
int r = idx / cols;
int c = idx % cols;
visit(r + 1, c, grid, queue);
visit(r - 1, c, grid, queue);
visit(r, c + 1, grid, queue);
visit(r, c - 1, grid, queue);
}
}
private void visit(int i, int j, char[][] grid, LinkedList<Integer> queue) {
if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] != '1')
return;
grid[i][j] = '2';
queue.offer(i * grid[0].length + j);
}
}