LeetCode 200:岛屿数量 Number of Islands
题目:
给定一个由 '1'
(陆地)和 '0'
(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。
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:
输入:
11110
11010
11000
00000
输出: 1
示例 2:
输入:
11000
11000
00100
00011
输出: 3
解题思路:
首先明白岛屿的定义:一块 1 周围全是 0,即为一个岛屿。(注意:grid 数组内的 1、0 均为char型字符,非整型)
示例1 中所有 1 都可以连接到一起,即所有 1 组成一个岛屿。示例2 中的三个岛屿:左上角四个1、中间一个1、右下角一个一,分别组成三个岛屿。
Flood fill算法是从一个区域中提取若干个连通的点与其他相邻区域区分开(或分别染成不同颜色)的经典算法。因为其思路类似洪水从一个区域扩散到所有能到达的区域而得名。在 GNU Go 和 扫雷 中,Flood Fill算法被用来计算需要被清除的区域。由上述定义可看出该题是典型的Flood fill算法类型例题,将岛屿与水分开,并染成特定颜色,以记录已累加过该岛屿。
每块岛屿可以看成相连的一个个节点,只需把所有相连节点遍历殆尽并标上特殊值以记录该节点已访问过,则遍历殆尽时证明一块岛屿已找到。
三种解题方法:
DFS(深度优先搜索):从一个为1的根节点开始访问,从每个相邻1节点向下访问到顶点(周围全是水),依次访问其他相邻1节点到顶点
BFS(广度优先搜索):从一个为1的根节点开始访问,每次向下访问一个节点,直到访问到最后一个顶点
并查集:也被称为联合查找数据结构,因为它主要由联合、查找两个过程实现:
Find:确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集。
Union:将两个子集合并成同一个集合。
针对该题即 先以一个根节点1作为初始节点,判断周围节点是否为1,如果是则新建一个集合并把该节点作为父节点。之后遍历下一个节点,如果是1则查找该节点的父节点(即第一个节点),并把该节点周围为1的节点的父节点全部指向该节点的父节点,以此类推,直到把该块岛屿所有1 节点加入同一个集合。最后集合个数(父节点的个数)即为岛屿数量
DFS:
时间复杂度 : O(M×N),其中 M 和 N 分别为行数和列数。
空间复杂度 : 最坏情况下为 O(M×N),此时整个网格均为陆地,深度优先搜索的深度达到 M×N。
Java:
class Solution {
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) return 0;
int row = grid.length, columns = grid[0].length, count = 0;
for (int i = 0; i < row; i++) {//遍历所有点
for (int j = 0; j < columns; j++) {
if (grid[i][j] == '1') {
dfs(grid, i, j, row, columns);//dfs遍历所有连接的点
count++;//记录岛屿数量
}
}
}
return count;
}
private void dfs(char[][] grid, int i, int j, int row, int columns) {
if (i < 0 || j < 0 || i >= row || j >= columns || grid[i][j] == '0') return;//基线条件
grid[i][j] = '0';//遍历过的点置 0
dfs(grid, i + 1, j, row, columns);
dfs(grid, i, j + 1, row, columns);
dfs(grid, i - 1, j, row, columns);
dfs(grid, i, j - 1, row, columns);
}
}
Python:
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
if not grid or len(grid) == 'o': return 0
row, columns = len(grid), len(grid[0])
count = 0
for i in range(row):
for j in range(columns):
if grid[i][j] == '1':
self.dfs(grid, i, j, row, columns)
count += 1
return count
def dfs(self, grid: List[List[str]], i: int, j: int, row: int, columns: int):
if i >= row or i < 0 or j >= columns or j < 0 or grid[i][j] == '0': return
grid[i][j] = '0'
self.dfs(grid, i - 1, j, row, columns)
self.dfs(grid, i, j - 1, row, columns)
self.dfs(grid, i + 1, j, row, columns)
self.dfs(grid, i, j + 1, row, columns)
BFS:
时间复杂度 : O(M×N),其中 M 和 N 分别为行数和列数。
空间复杂度 : O( min(M,N) ),在最坏的情况下(全部为陆地),队列的大小可以达到 min(M,N)。
Java:
class Solution {
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) return 0;
int row = grid.length, columns = grid[0].length, count = 0;
for (int i = 0; i < row; i++) {
for (int j = 0; j < columns; j++) {//遍历所有节点
if (grid[i][j] == '1') {
bfs(grid, i, j, row, columns);
count++;//记录岛屿数量
}
}
}
return count;
}
private void bfs(char[][] grid, int i, int j, int row, int columns) {
Queue<Integer> loc = new LinkedList<>();//队列暂存值为 1 的点
loc.add(i * columns + j);//暂存该点位置,也可以用一个[i,j]数组表示,不过占用空间也会大一倍
while (!loc.isEmpty()) {
int id = loc.remove();//取出位置
int r = id / columns, c = id % columns;//分解位置得到索引
if (r - 1 >= 0 && grid[r - 1][c] == '1') {
loc.add((r - 1) * columns + c);
grid[r - 1][c] = '0';
}
if (r + 1 < row && grid[r + 1][c] == '1') {
loc.add((r + 1) * columns + c);
grid[r + 1][c] = '0';
}
if (c - 1 >= 0 && grid[r][c - 1] == '1') {
loc.add(r * columns + c - 1);
grid[r][c - 1] = '0';
}
if (c + 1 < columns && grid[r][c + 1] == '1') {
loc.add(r * columns + c + 1);
grid[r][c + 1] = '0';
}
}
}
}
Python3:
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
if not grid or len(grid) == 'o': return 0
row, columns = len(grid), len(grid[0])
count = 0
for i in range(row):
for j in range(columns):
if grid[i][j] == '1':
self.bfs(grid, i, j, row, columns)
count += 1
return count
def bfs(self, grid: List[List[str]], i: int, j: int, row: int, columns: int):
queue = collections.deque()
queue.append((i, j)) # 位置以元组存入队列
while queue:
r, c = queue.popleft()
if r + 1 < row and grid[r + 1][c] == '1':
queue.append((r + 1, c))
grid[r + 1][c] = '0'
if r - 1 >= 0 and grid[r - 1][c] == '1':
queue.append((r - 1, c))
grid[r - 1][c] = '0'
if c + 1 < columns and grid[r][c + 1] == '1':
queue.append((r, c + 1))
grid[r][c + 1] = '0'
if c - 1 >= 0 and grid[r][c - 1] == '1':
queue.append((r, c - 1))
grid[r][c - 1] = '0'
并查集:
并查集这种解法冗杂且鸡肋,效率很低,以下java代码参考自LeetCode。简单了解其思想扩展一下思路即可:
Java:
class Solution {
class UnionFind {
int count; //计数
int[] parent;
int[] rank;
public UnionFind(char[][] grid) {
count = 0;
int m = grid.length, n = grid[0].length;
parent = new int[m * n];
rank = new int[m * n];
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] == '1') {
parent[i * n + j] = i * n + j;
++count;
}
rank[i * n + j] = 0;
}
}
}
public int find(int i) {
if (parent[i] != i) parent[i] = find(parent[i]);
return parent[i];
}
public void union(int x, int y) {
int rootx = find(x);
int rooty = find(y);
if (rootx != rooty) {
if (rank[rootx] > rank[rooty]) {
parent[rooty] = rootx;
} else if (rank[rootx] < rank[rooty]) {
parent[rootx] = rooty;
} else {
parent[rooty] = rootx;
rank[rootx] += 1;
}
--count;
}
}
public int getCount() {
return count;
}
}
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) return 0;
int row = grid.length, columns = grid[0].length;
UnionFind uf = new UnionFind(grid);
for (int i = 0; i < row; ++i) {
for (int j = 0; j < columns; ++j) {
if (grid[i][j] == '1') {
grid[i][j] = '0';
if (i - 1 >= 0 && grid[i - 1][j] == '1') uf.union(i * columns + j, (i - 1) * columns + j);
if (i + 1 < row && grid[i + 1][j] == '1') uf.union(i * columns + j, (i + 1) * columns + j);
if (j - 1 >= 0 && grid[i][j - 1] == '1') uf.union(i * columns + j, i * columns + j - 1);
if (j + 1 < columns && grid[i][j + 1] == '1') uf.union(i * columns + j, i * columns + j + 1);
}
}
}
return uf.getCount();
}
}
欢迎关注微.信公.众号一起加油吖:爱写Bug
LeetCode 200:岛屿数量 Number of Islands的更多相关文章
- Java实现 LeetCode 200 岛屿数量
200. 岛屿数量 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量.一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的.你可以假设网格的四个边均被水包围. ...
- 力扣Leetcode 200. 岛屿数量
岛屿数量 给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量. 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成. 此外,你可以假设该网 ...
- LeetCode 200. 岛屿数量
习题地址 https://leetcode-cn.com/problems/number-of-islands/ 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量.一个岛被水 ...
- Leetcode之深度优先搜索(DFS)专题-200. 岛屿数量(Number of Islands)
Leetcode之深度优先搜索(DFS)专题-200. 岛屿数量(Number of Islands) 深度优先搜索的解题详细介绍,点击 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计 ...
- Leetcode 200.岛屿的数量 - DFS、BFS
Leetcode 200 岛屿的数量: DFS利用函数调用栈保证了检索顺序, BFS则需要自己建立队列,把待检索对象按规则入队. class Solution { // DFS解法,8ms/10.7M ...
- [LeetCode]695. 岛屿的最大面积(DFS/BFS)、200. 岛屿数量(DFS/BFS待做/并差集待做)
695. 岛屿的最大面积 题目 给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合.你可以假设二维矩阵的四个边缘都被 ...
- Leetcode题目200.岛屿数量(BFS+DFS+并查集-中等)
题目描述: 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量.一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的.你可以假设网格的四个边均被水包围. 示例 ...
- Leetcode 200. 岛屿的个数(扩展)
1.题目描述 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量.一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的.你可以假设网格的四个边均被水包围. 示例 ...
- LeetCode 200. 岛屿的个数(Number of Islands)
题目描述 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量.一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的.你可以假设网格的四个边均被水包围. 示例 1 ...
随机推荐
- Windows安装与配置—MongoDB
1,下载安装 打开下载链接:http://dl.mongodb.org/dl/win32/x86_64,选择后缀是2008plus-ssl-3.6.15.zip的版本,32位和64位通用. 2,安装配 ...
- XML与DTD(够用)
1: 概述 1.1 什么是XML 1.2 三个重点 1.3规则 1.4 常用转义 2: Xml声明 XML 中,空格会被保留 XML 以 LF 存储换行 3:Xml标签 4:Xml元素 5:XML 属 ...
- Chapter 2 :重构的原则
1,什么是重构? 在不改变软件可观察行为的前提下,使用一些重构的手法,提高代码可读性. 换句话说,在保持软件可用的前提下,修改代码使得更加容易被理解. 2,为什么重构? 为了后续的代码维护和修改,易读 ...
- 微信小程序反编译
看到一个有意思的小程序,想了解是如何实现的,于是找了反编译方法. 安装adb驱动 百度安装adb驱动, 设计设置开发者模式,连接电脑. -> % adb devices List of devi ...
- oracle学习笔记(十) 查询练习(一)
查询练习一 表创建 create table employee as select * from soctt.emp ; --记得授权 sysdba用户登录 grant select on scott ...
- shell 命名管道,进程间通信, ncat作http server
命名管道基础 命名管道也被称为FIFO文件, 在文件系统中是可见的,并且跟其它文件一样可以读写! 命名管道特点: 当写进程向管道中写数据的时候,如果没有进程读取这些数据,写进程会堵塞 当读取管道中的数 ...
- sql server 列字段拼接 —— STUFF
原始数据: sql语句 SELECT DISTINCT l.family_id, )) ,,'' ) isc_id FROM dbo.Addresses l 结果数据:
- java基础(24):转换流、缓冲流
1. 转换流 在学习字符流(FileReader.FileWriter)的时候,其中说如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamReader或者OutputS ...
- 用Python进行数据清洗,这7种方法你一定要掌握
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者 | 常国珍.赵仁乾.张秋剑 来源 |<Python数据科学:技术 ...
- JQ中的Ajax的封装
1.认识JQ中ajax的封装 jQ 对于ajax的封装有两层实现:$.ajax 为底层封装实现:基于 $.ajax ,分别实现了$.get 与$.post 的高层封装实现: 2.Ajax的底 ...