Leetcode200、岛屿数量
下面将要使用三个方法来解题
方法一、深度优先遍历
时间复杂度:O(MN),其中 MM 和 NN 分别为行数和列数
空间复杂度:O(MN),visited数组
class Solution {
/**
1、深度优先遍历
每一次dfs都是把相邻位置的岛屿变成已经访问过,可以进行几次dfs,
就有几个岛屿。
*/
int[][] dirs = {{-1,0},{1,0},{0,-1},{0,1}};
public int numIslands(char[][] grid) {
int m = grid.length, n = grid[0].length;
boolean[][] visited = new boolean[m][n];
int ans = 0;
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
if(grid[i][j] == '1' && !visited[i][j]) {
dfs(grid, visited, i, j);
ans++;
}
}
}
return ans;
}
/**
需要注意的点:在进入方法的时候,除了要判断row和column是否合法以及当前位置是否已经遍历过以外,还需要
判断当前位置是不是岛屿,不是岛屿也要返回。
*/
public void dfs(char[][] grid, boolean[][] visited, int row, int column) {
if(row < 0 || row >= grid.length || column < 0 || column >= grid[0].length || visited[row][column]
|| grid[row][column] == '0') {
return;
}
if(grid[row][column] == '1') {
visited[row][column] = true;
}
for(int i = 0; i < 4; i++) {
int r = row + dirs[i][0];
int c = column + dirs[i][1];
dfs(grid, visited, r, c);
}
}
}
方法二、广度优先遍历
时间复杂度:O(MN),其中 MM 和 NN 分别为行数和列数
空间复杂度:O(MN),visited数组
class Solution {
int[][] dirs = {{-1,0},{1,0},{0,-1},{0,1}};
/**
2、广度优先遍历
*/
public int numIslands(char[][] grid) {
Queue<int[]> queue = new LinkedList<>();
int m = grid.length, n = grid[0].length;
boolean[][] visited = new boolean[m][n];
int ans = 0;
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
if(grid[i][j] == '1' && !visited[i][j]) {
queue.offer(new int[]{i, j});
while(!queue.isEmpty()) {
int[] curr = queue.poll();
for(int k = 0; k < 4; k++) {
int r = curr[0] + dirs[k][0];
int c = curr[1] + dirs[k][1];
// 判断当前坐标的合法性。
if(r < 0 || r >= m || c <0 || c >= n || visited[r][c] || grid[r][c] == '0') {
continue;
}else{
visited[r][c] = true;
queue.offer(new int[]{r, c});
}
}
}
ans++;
}
}
}
return ans;
}
}
方法三、并查集
时间复杂度: O(MN×α(MN))
空间复杂度:O(MN)是并查集需要的空间
对于所有的并查集的方法来说,包括两个方法:find(查询两个元素是否在一个集合里面)和union(如果两个元素符合一个集合,就进行合并)
定义parent数组,开始的时候每一个部分属于一个子集
/**
3、并查集
*/
class UnionFind {
int count;
int[] parent;
int[] rank;
public UnionFind(char[][] grid) {
count = 0;
int m = grid.length;
int n = grid[0].length;
parent = new int[m * n];
rank = new int[m * n];
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
// 开始的时候,把每一个岛屿都看成是一个独立的子集。
if (grid[i][j] == '1') {
parent[i * n + j] = i * n + j;
++count;
}
rank[i * n + j] = 0;
}
}
}
// 寻找当前位置的最最最祖先的节点,这样最后计算岛屿数量的时候,只要当前位置为1且当前位置的值没有发生变化,
// 就是一个岛屿。
public int find(int i) {
if (parent[i] != i) parent[i] = find(parent[i]);
return parent[i];
}
// 合并两个位置,谁rank比较大,谁是祖先,合并了count就要--,代表两个岛屿合并成了一个岛屿。
public void union(int x, int y) {
int rootx = find(x);
int rooty = find(y);
if (rootx != rooty) {
if (rank[rootx] > rank[rooty]) {
parent[rooty] = rootx;
} else if (rank[rootx] < rank[rooty]) {
parent[rootx] = rooty;
} else {
parent[rooty] = rootx;
rank[rootx] += 1;
}
--count;
}
}
public int getCount() {
return count;
}
}
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) {
return 0;
}
int nr = grid.length;
int nc = grid[0].length;
int num_islands = 0;
UnionFind uf = new UnionFind(grid);
for (int r = 0; r < nr; ++r) {
for (int c = 0; c < nc; ++c) {
if (grid[r][c] == '1') {
grid[r][c] = '0';
// 分别遍历当前位置的上下左右,使用union进行合并。
if (r - 1 >= 0 && grid[r-1][c] == '1') {
uf.union(r * nc + c, (r-1) * nc + c);
}
if (r + 1 < nr && grid[r+1][c] == '1') {
uf.union(r * nc + c, (r+1) * nc + c);
}
if (c - 1 >= 0 && grid[r][c-1] == '1') {
uf.union(r * nc + c, r * nc + c - 1);
}
if (c + 1 < nc && grid[r][c+1] == '1') {
uf.union(r * nc + c, r * nc + c + 1);
}
}
}
}
return uf.getCount();
}
本文详细介绍了LeetCode200题的解决方案,包括深度优先遍历、广度优先遍历和并查集三种方法。每个方法的时间和空间复杂度都进行了分析,深度优先和广度优先的时间复杂度皆为O(MN),空间复杂度为O(MN);并查集的时间复杂度为O(MN*α(MN)),空间复杂度为O(MN)。文中还解释了并查集中的find和union操作。
1035

被折叠的 条评论
为什么被折叠?



