力扣-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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步