【问题描述】 第200题 岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外,你可以假设该网格的四条边均被水包围。示例 1:输入:
11110
11010
11000
00000
输出: 1
示例 2:输入:
11000
11000
00100
00011
输出: 3
解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。

【解答思路】

1. 深度优先遍历

时间复杂度:O(N^2) 空间复杂度:O(N)

/*** 方法一:深度优先遍历*/
public class Solution {//           x-1,y//  x,y-1    x,y      x,y+1//           x+1,y// 方向数组,它表示了相对于当前位置的 4 个方向的横、纵坐标的偏移量,这是一个常见的技巧private static final int[][] directions = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};// 标记数组,标记了 grid 的坐标对应的格子是否被访问过private boolean[][] marked;// grid 的行数private int rows;// grid 的列数private int cols;private char[][] grid;public int numIslands(char[][] grid) {rows = grid.length;if (rows == 0) {return 0;}cols = grid[0].length;this.grid = grid;marked = new boolean[rows][cols];int count = 0;for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {// 如果是岛屿中的一个点,并且没有被访问过// 就进行深度优先遍历if (!marked[i][j] && grid[i][j] == '1') {count++;dfs(i, j);}}}return count;}// 从坐标为 (i,j) 的点开始进行深度优先遍历private void dfs(int i, int j) {marked[i][j] = true;// 得到 4 个方向的坐标for (int k = 0; k < 4; k++) {int newX = i + directions[k][0];int newY = j + directions[k][1];// 如果不越界、没有被访问过、并且还要是陆地if (inArea(newX, newY) && grid[newX][newY] == '1' && !marked[newX][newY]) {dfs(newX, newY);}}}// 封装成 inArea 方法语义更清晰private boolean inArea(int x, int y) {// 等于号不要忘了return x >= 0 && x < rows && y >= 0 && y < cols;}public static void main(String[] args) {Solution solution = new Solution();char[][] grid1 = {{'1', '1', '1', '1', '0'},{'1', '1', '0', '1', '0'},{'1', '1', '0', '0', '0'},{'0', '0', '0', '0', '0'}};int numIslands1 = solution.numIslands(grid1);System.out.println(numIslands1);char[][] grid2 = {{'1', '1', '0', '0', '0'},{'1', '1', '0', '0', '0'},{'0', '0', '1', '0', '0'},{'0', '0', '0', '1', '1'}};int numIslands2 = solution.numIslands(grid2);System.out.println(numIslands2);}
}
2. 广度优先遍历

时间复杂度:O(N) 空间复杂度:O(N)

import java.util.LinkedList;/*** 方法二:广度优先遍历*/
public class Solution2 {private int rows;private int cols;public int numIslands(char[][] grid) {//           x-1,y//  x,y-1    x,y      x,y+1//           x+1,yint[][] directions = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};rows = grid.length;if (rows == 0) {return 0;}cols = grid[0].length;boolean[][] marked = new boolean[rows][cols];int count = 0;for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {// 如果是岛屿中的一个点,并且没有被访问过// 从坐标为 (i,j) 的点开始进行广度优先遍历if (!marked[i][j] && grid[i][j] == '1') {count++;LinkedList<Integer> queue = new LinkedList<>();// 小技巧:把坐标转换为一个数字// 否则,得用一个数组存,在 Python 中,可以使用 tuple 存queue.addLast(i * cols + j);// 注意:这里要标记上已经访问过marked[i][j] = true;while (!queue.isEmpty()) {int cur = queue.removeFirst();int curX = cur / cols;int curY = cur % cols;// 得到 4 个方向的坐标for (int k = 0; k < 4; k++) {int newX = curX + directions[k][0];int newY = curY + directions[k][1];// 如果不越界、没有被访问过、并且还要是陆地,我就继续放入队列,放入队列的同时,要记得标记已经访问过if (inArea(newX, newY) && grid[newX][newY] == '1' && !marked[newX][newY]) {queue.addLast(newX * cols + newY);// 【特别注意】在放入队列以后,要马上标记成已经访问过,语义也是十分清楚的:反正只要进入了队列,你迟早都会遍历到它// 而不是在出队列的时候再标记// 【特别注意】如果是出队列的时候再标记,会造成很多重复的结点进入队列,造成重复的操作,这句话如果你没有写对地方,代码会严重超时的marked[newX][newY] = true;}}}}}}return count;}private boolean inArea(int x, int y) {// 等于号这些细节不要忘了return x >= 0 && x < rows && y >= 0 && y < cols;}public static void main(String[] args) {Solution2 solution2 = new Solution2();char[][] grid1 = {{'1', '1', '1', '1', '0'},{'1', '1', '0', '1', '0'},{'1', '1', '0', '0', '0'},{'0', '0', '0', '0', '0'}};int numIslands1 = solution2.numIslands(grid1);System.out.println(numIslands1);char[][] grid2 = {{'1', '1', '0', '0', '0'},{'1', '1', '0', '0', '0'},{'0', '0', '1', '0', '0'},{'0', '0', '0', '1', '1'}};int numIslands2 = solution2.numIslands(grid2);System.out.println(numIslands2);}
}
3. 并查集

public class Solution {

public int numIslands(char[][] grid) {int rows = grid.length;if (rows == 0) {return 0;}int cols = grid[0].length;int size = rows * cols;// 两个方向的方向向量(理解为向下和向右的坐标偏移)int[][] directions = {{1, 0}, {0, 1}};// +1 是认为虚拟的水域UnionFind unionFind = new UnionFind(size + 1);for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {if (grid[i][j] == '1') {for (int[] direction : directions) {int newX = i + direction[0];int newY = j + direction[1];if (newX < rows && newY < cols && grid[newX][newY] == '1') {unionFind.union(cols * i + j, cols * newX + newY);}}} else {// 如果不是陆地,所有的水域和一个虚拟的水域连接unionFind.union(cols * i + j, size);}}}// 减去那个一开始多设置的虚拟的水域return unionFind.count - 1;
}class UnionFind {private int[] parent;private int count;public UnionFind(int n) {this.count = n;parent = new int[n];for (int i = 0; i < n; i++) {parent[i] = i;}}/*** 返回索引为 p 的元素的根结点** @param p* @return*/public int find(int p) {// 在 find 的时候执行路径压缩while (p != parent[p]) {// 两步一跳完成路径压缩,这里是「隔代压缩」// 说明:「隔代压缩」和「按秩合并」选择一个实现即可,「隔代压缩」的代码量少,所以选它parent[p] = parent[parent[p]];p = parent[p];}return p;}public boolean connected(int p, int q) {int pRoot = find(p);int qRoot = find(q);return pRoot == qRoot;}public void union(int p, int q) {int pRoot = find(p);int qRoot = find(q);if (pRoot == qRoot) {return;}parent[qRoot] = pRoot;// 每次 union 以后,连通分量减 1count--;}
}

}

【总结】

1. 深度遍历
  • 递归
2. 广度遍历
  • 队列
  • 所有加入队列的结点,都应该马上被标记为 “已经访问”,否则有可能会被重复加入队列
3.并查集学习资料

https://liweiwei1419.gitee.io/leetcode-algo/leetcode-by-tag/union-find/

参考链接:https://leetcode-cn.com/problems/number-of-islands/solution/dfs-bfs-bing-cha-ji-python-dai-ma-java-dai-ma-by-l/

[Leedcode][JAVA][第200题][岛屿数量][DFS][BFS][并查集]相关推荐

  1. [Leedcode][JAVA][第22题括号生成][DFS][BFS][动态规划]

    [问题描述]22. 括号生成 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合. 示例:输入:n = 3 输出:["((()))",&q ...

  2. [Leedcode][JAVA][第990题][等式方程的可满足性][并查集]

    [问题描述][中等] 给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4,并采用两种不同的形式之一:"a==b" 或 &quo ...

  3. hdu1181变形课dfs/bfs/并查集三种解法(java)

    题目链接 Problem Description 呃-变形课上Harry碰到了一点小麻烦,因为他并不像Hermione那样能够记住所有的咒语而随意的将一个棒球变成刺猬什么的,但是他发现了变形咒语的一个 ...

  4. hdu dfs入门java_hdu1181变形课dfs/bfs/并查集三种解法(java)

    题目链接 Problem Description 呃-变形课上Harry碰到了一点小麻烦,因为他并不像Hermione那样能够记住所有的咒语而随意的将一个棒球变成刺猬什么的,但是他发现了变形咒语的一个 ...

  5. LeetCode 305. 岛屿数量 II(并查集)

    文章目录 1. 题目 2. 解题 2.1 超时解 2.1 改进计算方法 1. 题目 假设你设计一个游戏,用一个 m 行 n 列的 2D 网格来存储你的游戏地图. 起始的时候,每个格子的地形都被默认标记 ...

  6. 岛屿的个数java_LeetCode 200:岛屿数量 Number of Islands

    题目: 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量.一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的.你可以假设网格的四个边均被水包围. Given ...

  7. [Leedcode][JAVA][第105题][从前序与中序遍历序列构造二叉树][栈][递归][二叉树]

    [问题描述][中等] 根据一棵树的前序遍历与中序遍历构造二叉树.注意: 你可以假设树中没有重复的元素.例如,给出前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = ...

  8. [Leedcode][JAVA][第470题][Ran7()实现Rand10()]

    [问题描述][Leedcode][JAVA][第470题][Ran7()实现Rand10()] 已有方法 rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10 生成 1 到 ...

  9. [Leedcode][JAVA][第45题][跳跃游戏 II][贪心算法]

    [问题描述][Leedcode][JAVA][第45题][跳跃游戏 II] 输入: [2,3,1,1,4] 输出: 2 解释: 跳到最后一个位置的最小跳跃数是 2.从下标为 0 跳到下标为 1 的位置 ...

最新文章

  1. angularjs 的笔记
  2. python怎么建文件dome_Python专题(四) 如何制作一个demo给老板看
  3. GridView简单创建序号列
  4. 易语言添加ctrl c键,易语言操作快捷键汇总
  5. react实现全选和反选_全选的实现
  6. CMD终端关于pip报错,scrapy报错的一种处理方法
  7. jmeter性能测试实战_Jmeter接口测试实战篇:10分钟学会Jmeter的用法
  8. 要搞懂 volatile 关键字,就靠这 26 张图
  9. OpenCV-图像处理(29、凸包-Convex Hull)
  10. linux下添加三菱触摸屏usb驱动,[转载]三菱触摸屏GT Works3和PLC GX Works2编程软件下载...
  11. 【原创】SWOT分析思维的一些基本思考与见解
  12. html5中的function,js中function函数的使用方法
  13. 正态分布之中心极限定理
  14. 在下拉列表框上添加--请选择--
  15. 判断鼠标滑轮滚动事件
  16. 【WSN通信】能量均衡的无线传感器网络非均匀分簇路由协议附matlab代码
  17. 直接灰度变换法matlab,数字图像处理-灰度变换(附MATLAB代码)
  18. Risk of rain 2 Linux 服务器搭建
  19. 【金融】新成立基金建仓时点、行业分布与市场行情关系探究
  20. 利用python爬取微博热搜并进行数据分析

热门文章

  1. caffe源码阅读(1)_整体框架和简介(摘录)
  2. 刚学unity3d,跟着仿作了flappy bird,记下一些琐碎的心得!
  3. 如何使用一个库中不存在的函数
  4. .net Redis缓存优化提高加载速度和服务器性能(一)
  5. 蓝桥杯java能用编译器1吗_学java的你,这些英文单词都掌握了吗?
  6. AAPT2 error: check logs for details.
  7. ssh报错解决 ECDSA host key for 123.56.11.181 has changed and you have requested strict checking.
  8. 从底层重学 Java 之四大整数 GitChat链接
  9. FastDFS java api调用
  10. json 微信小程序 筛选_微信小程序学习记录