题目分析:
在二维空间中,只需要讨论每个1的上下左右是否为1即可。而利用并查集的方法,可以设每一个1是一个单独的集合,然后从第一行第一列依次往m行n列向四周寻找,当邻接元素为1时,则合并为同一个集合,遇到零则终止该行或列,最后再统计场上剩余的集合数量,便得到岛屿数量;
优化思路:
1,在寻找邻接的元素是否为1的时候,不需要上下左右四个方向的寻找,只需要向上或者向左查询是否为1,然后判断是否合并为同一个集合。因为当依次遍历查找元素的时候,查找的顺序是始终向右以及向下的,既这个元素无论如何都会被判断一次是否为1,便不需要再向右或下判断是否同为1,当遍历到这个数时,向上或向左判断就足够确认是否在同一个集合内了,即使不向右判断,下一次遍历也会跑到右边的元素身上,然后再向左向上判断是否为同为1(向下同理);
2,存储二维空间的坐标的时候,不一定需要用二维数组来存储,可以从第一个位置按照遍历的顺序,依次编号,使得在查询第 i 行第 j 列(默认从第0行第0列开始,共有 n 行 m 列)的元素时,使用公式来计算出他的一维对应位置坐标:id=i*m+j;
代码实现:
#include <vector>
using namespace std;
class Code05_NumberOfIslands {
public:
int numIslands(vector<vector<char>>& board) {
int n = board.size();//矩阵的行数
int m = board[0].size();//矩阵中第一行的元素个数,既列数
build(n, m, board);//记录每一个值为1的数据
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (board[i][j] == '1') {
if (j > 0 && board[i][j - 1] == '1') {
union(i, j, i, j - 1);//合并两个坐标进入一个集合
}
if (i > 0 && board[i - 1][j] == '1') {
union(i, j, i - 1, j);
}
}
}
}
return sets;//多少个集合,既岛屿
}
private:
static const int MAXSIZE = 100001;
static int father[MAXSIZE];
static int cols;
static int sets;
void build(int n, int m, vector<vector<char>>& board) {
cols = m;
sets = 0;
for (int a = 0; a < n; a++) {
for (int b = 0, index; b < m; b++) {
if (board[a][b] == '1') {
index = indexe(a, b);
father[index] = index;将自己的父亲指向自己,既自己是个独立的集合
sets++;
}
}
}
}
int indexe(int a, int b) {
return a * cols + b;//计算该坐标的一维id
}
int find(int i) {
if (i != father[i]) {
father[i] = find(father[i]);//找到自己的集合的根节点,也就是这个集合用来连接下一个集合的最后一位
}
return father[i];
}
void union(int a, int b, int c, int d) {
int fx = find(indexe(a, b));
int fy = find(indexe(c, d));
if (fx != fy) {
father[fx] = fy;//连接二者
sets--;
}
}
};