【中等】200-岛屿数量 Number of Islands
题目
Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
Example1
Input:
11110
11010
11000
00000
Output: 1
Example2
Input:
11000
11000
00100
00011
Output: 3
来源:力扣(LeetCode)
链接:https://leetcode.com/problems/number-of-islands
解法
方法一:深度优先搜索
解题思路
遍历表格,只要出现一个岛屿(出现一个1),就把这个岛屿从表格中去掉(把这个1及其周围所有相邻的1都变为0)并记录下这个岛屿(岛屿数+1),直到表格里面没有岛屿(没有字符为1的位置),就完成了所有岛屿的计数。
搜索岛屿的过程采用深度优先搜索的方法,出现1时,其变为0,然后分别检查其周围的4个位置,当有1出现时,将其变为0,并继续检查其周边四个位置,不断重复这个过程就是深度优先搜索。
注意:递归代码一定要先完成当前位置的修改再递归
代码
class Solution {
public:
void clear(vector<vector<char>>& grid,int i,int j){
grid[i][j] = '0';
if (i > 0 && grid[i-1][j] == '1') clear(grid, i-1, j);
if (i < grid.size()-1 && grid[i+1][j] == '1') clear(grid, i+1, j);
if (j > 0 && grid[i][j-1] == '1') clear(grid, i, j-1);
if (j < grid[0].size()-1 && grid[i][j+1] == '1') clear(grid, i, j+1);
}
int numIslands(vector<vector<char>>& grid) {
int line = grid.size();
if(line==0) return 0;
int row = grid[0].size(), num = 0;
for(int i = 0; i < line; ++i){
for(int j = 0; j < row; ++j){
if(grid[i][j] == '1'){
num++;
clear(grid,i,j);
}
}
}
return num;
}
};
方法二:广度优先搜索
解题思路
和深度优先搜索的思路是一样的,都是一次搜索一座岛屿。在代码编写时,如果遇到1,先将其变为0,添加岛屿数,然后把其周围的1放入一个队列,再修改并弹出队列的第一个元素,并将元素周围的1再放入这个队列的末尾,重复直到队列为空,就完成了一座岛屿的搜索
代码
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int line = grid.size();
if(line==0) return 0;
int row = grid[0].size(), num = 0;
for(int i = 0; i < line; ++i){
for(int j = 0; j < row; ++j){
if(grid[i][j] == '1'){
num++;
grid[i][j] = '0';
queue<pair<int, int>> q;
q.push(pair<int, int>(i, j));
while(!q.empty()){
int l = q.front().first, r = q.front().second;
q.pop();
if (l > 0 && grid[l-1][r] == '1') {
q.push(pair<int, int>(l-1, r));
grid[l-1][r] = '0';
}
if (l < line-1 && grid[l+1][r] == '1'){
q.push(pair<int, int>(l+1, r));
grid[l+1][r] = '0';
}
if (r > 0 && grid[l][r-1] == '1') {
q.push(pair<int, int>(l, r-1));
grid[l][r-1] = '0';
}
if (r < row-1 && grid[l][r+1] == '1'){
q.push(pair<int, int>(l, r+1));
grid[l][r+1] = '0';
}
}
}
}
}
return num;
}
};
方法三:并查集搜索
解法
并查集搜索的核心是树结构,即如果两个节点能够找到相同的根节点,那么这两个点一定在同一个树上。也就是说,如果两个1能够找到相同的根,那么他们就在同一个岛屿上,所以,我们只需要给每一个岛屿留下一个代表,该岛屿上其他所有的1都被合并进来,最后岛屿数就等于1的总数-被合并的1数
由此,我们用一个数组表示一个点1是与其前方哪一个1在同一座岛屿上的,初始化均为自己,通过不断重复寻找接壤的点,就可以找到最开始是在哪一座岛屿上,即如果点5与点4相邻,点5接壤于点6,点6接壤点是自己,点4接壤于点2接壤于点1接壤,点1仍旧是自己,那么6一定在1上(1一定在6上),即记录点6接壤于点1
我们从上而下从左至右搜索每一个1,检查其右侧和下方是否有1可以与其合并,如果可以合并就将其与当前的1进行合并,当然合并的前提是他们当前并不在同一座岛屿上
代码
class Solution {
class UF{
private:
int island_num;
int* parent; // 记录接壤点
public:
UF(vector<vector<char>>& grid){
island_num = 0;
int line = grid.size(), row = grid[0].size();
parent = new int[line*row];
for(int i = 0; i < line; ++i){
for(int j = 0; j < row; ++j){
if(grid[i][j] == '1'){ // 初始化
parent[i*row+j] = i*row+j;
island_num++;
}
}
}
}
int find(int x){ // 找到最初的接壤位置
if(parent[x] != x) return find(parent[x]);
return parent[x];
}
void uni(int x, int y){ // 合并两个点
int rootx = find(x);
int rooty = find(y);
if(rootx != rooty){
island_num--;
parent[rooty] = rootx;
}
}
int get_num(){
return island_num;
}
};
public:
int numIslands(vector<vector<char>>& grid) {
int line = grid.size();
if(line==0) return 0;
int row = grid[0].size();
UF uf(grid);
for(int l = 0; l < line; ++l){
for(int r = 0; r < row; ++r){
if(grid[l][r] == '1'){
if (l < line-1 && grid[l+1][r] == '1') uf.uni(l*row+r, (l+1)*row+r); //向下
if (r < row-1 && grid[l][r+1] == '1') uf.uni(l*row+r, l*row+r+1); // 向右
}
}
}
return uf.get_num();
}
};
总结
- 对于广度优先搜索方法,一定要在插入的时候将位置修改为‘0’,不能在弹出的时候,否则会超时(会重复向队列中加入一些位置的元素,所以要确保每个元素只能加入一次队列)