题目要求:
/**
*
* 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。
* 一个岛被水包围,并且它是通过水平方向或
* 垂直方向上相邻的陆地连接而成的。
* 你可以假设网格的四个边均被水包围。
*
* 示例 1:
* 输入:
* 11110
* 11010
* 11000
* 00000
* 输出: 1
*
* 示例 2:
* 输入:
* 11000
* 11000
* 00100
* 00011
* 输出: 3
*/
计算岛屿的数量可以认为是计算连通图的数量,使用宽度优先算法BFS来做是可以实现的
private static int[][] STEP = {{0,1},{0,-1},{1,0},{-1,0}};
public int numIslands(char[][] grid) {
int row = grid.length;
int num = 0;
if (row <= 0){
return 0;
}
int column = grid[0].length;
for (int i = 0; i < row; i++){
for (int j = 0; j < column; j++){
if (grid[i][j] != 49){
continue;
}
Queue queue = new LinkedList();
int[] point = {i,j};
queue.add(point);
while (!queue.isEmpty()){
int[] points = (int[])queue.poll();
// grid[points[0]][points[1]] = '2'; 在这里才设置已经遍历过,会导致很多循环,执行超时
for (int[] step : STEP){
int x = step[0] + points[0];
int y = step[1] + points[1];
if (x < 0 || x >= row || y < 0 || y >= column || grid[x][y] != 49){
continue;
}
int[] p = {x,y};
queue.add(p);
grid[x][y] = '2';
}
}
num ++;
}
}
return num;
}
刚开始设置已经遍历过的节点位置不对,看了题解才明白,如果在出队的时候才设置就会造成已经入队的节点重复入队增加了循环。
时间复杂度 : O(M×N)
空间复杂度 : O(min(M,N)
这道题其实也可以使用深度优先遍历和并查集来解决。
深度优先遍历:
使用递归,将一个1相邻的1全部遍历出来,就是一个岛,紧接着找到一个从来没有遍历过的1,开始重新递归,这样就能统计出所有的岛屿数。
代码如下:
public int numIslands(char[][] grid) {
int row = grid.length;
int num = 0;
if (row <= 0){
return 0;
}
int column = grid[0].length;
for (int i = 0; i < row; i++){
for (int j = 0; j < column; j++){
if (grid[i][j] != '1'){
continue;
}
dfs(grid,i,j,row,column);
num ++;
}
}
return num;
}
private void dfs(char[][] grid, int i, int j, int row, int column){
if (grid[i][j] == 49){
grid[i][j] = '2';
if (i+1<row){
dfs(grid, i+1,j,row,column);
}
if (j+1<column){
dfs(grid, i,j+1,row,column);
}
if (i-1>=0){
dfs(grid, i-1,j,row,column);
}
if (j-1>=0){
dfs(grid, i,j-1,row,column);
}
}
}
执行结果:
时间复杂度 : O(M×N)
空间复杂度 : 最坏情况下为 O(M×N)
并查集:
并查集的介绍:https://blog.csdn.net/tingtingyuan/article/details/81697698
这个举的例子很好理解,看一遍应该都能懂
public int numIslands(char[][] grid) {
if (grid == null || grid.length <= 0){
return 0;
}
NumLand numLand = new NumLand(grid);
int row = grid.length;
int column = grid[0].length;
for (int i = 0; i < row; i++){
for (int j = 0; j < column; j++){
if (grid[i][j] == '1'){
grid[i][j] = '0';
if (i - 1 >= 0 && grid[i-1][j] == '1'){
numLand.union(i * column + j, (i-1) * column + j);
}
if (j - 1 >= 0 && grid[i][j-1] == '1'){
numLand.union(i * column + j, i * column + j - 1);
}
if (i + 1 < row && grid[i+1][j] == '1'){
numLand.union(i * column + j, (i+1) * column + j);
}
if (j + 1 < column && grid[i][j+1] == '1'){
numLand.union(i * column + j, i * column + j + 1);
}
}
}
}
return numLand.getCount();
}
class NumLand {
private int[] parent;
private int[] rank;
private int count;
public NumLand(char[][] grid){
int row = grid.length;
int column = grid[0].length;
parent = new int[row * column];
rank = new int[row * column];
for (int i = 0; i < row; i++){
for (int j = 0; j < column; j++){
if (grid[i][j] == '1'){
parent[i * column + j] = i * column + j;
count ++;
}
}
}
}
public void union(int start, int end){
int rankX = find(start);
int rankY = find(end);
if (rankX != rankY){
if (rank[rankX] > rank[rankY]){
parent[rankY] = rankX;
}else if (rank[rankX] < rank[rankY]){
parent[rankX] = rankY;
}else{
parent[rankX] = rankY;
rank[rankY] += 1;
}
count --;
}
}
private int find(int i){
// 路径压缩,让每个元素都指向他的根节点
if (parent[i] != i){
parent[i] = find(parent[i]);
}
return parent[i];
}
public int getCount(){
return count;
}
}
复杂度分析
时间复杂度:O(M×N)。注意当使用路径压缩和排名结合并实现并查集时,并操作只需要消耗常数时间。
空间复杂度:O(M×N),这是并查集数据结构需要的空间。
在这个题里并查集的耗时并不是最优的可能与样例库有关。