2022-07-19 17:21阅读: 58评论: 0推荐: 0

力扣-200-岛屿数量

直达链接

岛屿数量

官方题解

那么在这里,题解1深度优先和题解2广度优先的区别在哪里呢?

深度优先遍历DFS

深度优先从一个节点开始,只要上下左右有一个为陆地就回向下递归,最终类似于一个树的递归结构
会有一个dfs辅助函数用于递归

/*
* 思路是这样:
* 遍历每一个单位点,如果遇到陆地就计数并开始dfs:将与这个陆地相连的所有陆地标记为0
*/
private:
int count = 0;
public:
void dfs(vector<vector<char>>& grid, int i, int j, int m, int n) {
if (i < 0 || i >= m || j < 0 || j >= n)return;
if (grid[i][j] == '0') return;
// 剩下就只有是1的情况
grid[i][j] = '0';
dfs(grid, i - 1, j, m, n);
dfs(grid, i + 1, j, m, n);
dfs(grid, i, j - 1, m, n);
dfs(grid, i, j + 1, m, n);
}
int numIslands(vector<vector<char>>& grid) {
int m = grid.size();
int n = grid[0].size();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
// 遍历每一个位置,如果是陆地就开始DFS标记
if (grid[i][j] == '1') {
dfs(grid, i, j, m, n);
count++;
}
}
}
return count;
}

广度优先遍历BFS

广度优先则是,每访问到一个为1的节点,就把它置零并放入队列,
只要队列不为空,取出一个,并把它上下左右这一圈中为1的放入队列,并且置0,实现一种“扩散”效果

那么,为什么需要这个队列?
因为要用它实现广度优先

并查集

并查集数据结构则是完美契合了这个题,一个岛屿就可以看作是一个1的并查集,那么岛屿的数量就是其中并查集的数量
要做的是扫描整个二维数组,将相连的1做“并”操作
实现,看来放入操作既不是深度也不是广度,它只会把上下左右为1的并入当前并查集,没有继续操作就直接往下访问下一个节点,但是只有在节点为1的时候才会创建新的并查集

class UnionFind {
private:
int count;// 用来记录树(即并查集)的个数
vector<int> parent;// 并查集的底层数组
vector<int> rank;// 秩,用来记录以各个数组元素为根节点的树的高度
public:
// 初始化方法
UnionFind(vector<vector<char>>& grid) {
count = 0;
int m = grid.size();
int n = grid[0].size();
// 扫描一遍整个二维数组,将值为1的插入并计数
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] == '1') {
// 为什么插入的是i*n+j?这是什么意思
// 这里是对一个二维数组构建并查集,就不能用下标值与下标相等的对应关系了
// 不对,这里的i*n+j还真不是乱写的,能将二维数组的双下标转换为一排一排顺序数来的下标
// 这样就又能够做到下标值与下标一致
parent.push_back(i * n + j);
++count;// 那这里岂不是有几个1,count就是几
// 是,但是后面每一次并操作count都会减一
}
else {
parent.push_back(-1);
}
rank.push_back(0);
}
}
}
// 返回根节点的值
int find(int i) {
// 根节点的元素值就等于数组下标
// 将要查找的元素作为下标去检查该位置的值
// 该位置饿值与下标不等,说明该节点不是根节点
if (parent[i] != i) {
parent[i] = find(parent[i]);
// 就递归地拿该位置值再作为下标去比对
// 路径压缩,彻底路径压缩,但是递归更消耗性能
}
// 最终得到下标值与下标相等的根节点
return parent[i];
}
void unite(int x, int y) {
// 先找到各自的根节点
int rootx = find(x);
int rooty = find(y);
if (rootx != rooty) {
// 当两个节点的根节点不是同一个根节点时
// 再把其中一个元素的根节点连接到另一个元素的根节点上
if (rank[rootx] < rank[rooty]) {
// 按秩合并,将高度(秩)较小的树连接到较大的树上面
swap(rootx, rooty);
}
// y的根节点认x的根节点为根
parent[rooty] = rootx;// 这是什么意思,把y的根节点的值改了,那么和下标就不一致了,就不是根节点了
// 为什么要加上这一句
// 如果他俩一样,仍然会有y被合并到x上面,那么x的高度加一
if(rank[rootx]==rank[rooty]) rank[rootx]+=1;
--count;// 合并后并查集数量减一
}
}
int getCount() {
return count;
}
};
class Solition {
public:
int numIslands(vector<vector<char>>& grid) {
int nr = grid.size();
if (!nr) return 0;
int nc = grid[0].size();
UnionFind uf(grid);
for (int r = 0; r < nr; ++r) {
for (int c = 0; c < nc; ++c) {
if (grid[r][c] == '1') {
grid[r][c] = '0';// 置零,避免被后续重复操作
// if中的第一个条件是判断是否越界的
// 如果上下左右某个位置为1,那么就将它与并查集合并
// 听说这里可以只判断两个方向
if (r - 1 >= 0 && grid[r - 1][c] == '1') uf.unite(r * nc + c, (r - 1) * nc + c);
if (r + 1 < nr && grid[r + 1][c] == '1') uf.unite(r * nc + c, (r + 1) * nc + c);
if (c - 1 >= 0 && grid[r][c - 1] == '1') uf.unite(r * nc + c, r * nc + c - 1);
if (c + 1 < nc && grid[r][c + 1] == '1') uf.unite(r * nc + c, r * nc + c + 1);
}
}
}
return uf.getCount();
}
};

本文作者:YaosGHC

本文链接:https://www.cnblogs.com/yaocy/p/16492791.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   YaosGHC  阅读(58)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起