题目:

给定一个由 '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的更多相关文章

  1. Java实现 LeetCode 200 岛屿数量

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

  2. 力扣Leetcode 200. 岛屿数量

    岛屿数量 给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量. 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成. 此外,你可以假设该网 ...

  3. LeetCode 200. 岛屿数量

    习题地址 https://leetcode-cn.com/problems/number-of-islands/ 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量.一个岛被水 ...

  4. Leetcode之深度优先搜索(DFS)专题-200. 岛屿数量(Number of Islands)

    Leetcode之深度优先搜索(DFS)专题-200. 岛屿数量(Number of Islands) 深度优先搜索的解题详细介绍,点击 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计 ...

  5. Leetcode 200.岛屿的数量 - DFS、BFS

    Leetcode 200 岛屿的数量: DFS利用函数调用栈保证了检索顺序, BFS则需要自己建立队列,把待检索对象按规则入队. class Solution { // DFS解法,8ms/10.7M ...

  6. [LeetCode]695. 岛屿的最大面积(DFS/BFS)、200. 岛屿数量(DFS/BFS待做/并差集待做)

    695. 岛屿的最大面积 题目 给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合.你可以假设二维矩阵的四个边缘都被 ...

  7. Leetcode题目200.岛屿数量(BFS+DFS+并查集-中等)

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

  8. Leetcode 200. 岛屿的个数(扩展)

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

  9. LeetCode 200. 岛屿的个数(Number of Islands)

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

随机推荐

  1. linux系列之常用运维命令整理笔录

    目录 本博客记录工作中需要的linux运维命令,大学时候开始接触linux,会一些基本操作,可是都没有整理起来,加上是做开发,不做运维,有些命令忘记了,所以现在整理成博客,当然vi,文件操作等就不介绍 ...

  2. pytest框架之parametries数据驱动参数化

    在测试用例的前面加上:@pytest.mark.parametrize('参数名', 列表数据) 参数名:用来接收每一项数据,并作为测试用例的参数 列表参数:一组测试数据(元组.列表.字典) 方式一: ...

  3. sql慢查询工具(配置代码)

    # 在mysql的配置文件/etc/mysql/mysql.conf.d/mysqld.cnf[mysqld]中配置懒查询 slow_query_log = ON # 是否已经开启慢查询 long_q ...

  4. route 相关设置

    Debian系统 查看路由表: root@debian:~# ip route default via 192.168.6.1 dev enp4s0 10.0.0.0/24 dev br0 proto ...

  5. C# 修改配置文件

    /// <summary> /// 保存配置文件的设定 /// </summary> /// <param name="Key"></pa ...

  6. C# Newtonsoft.Json 读取文件,返回json字符串

    第一种方法: public object getData2() { string content; using (StreamReader sr = new StreamReader(Server.M ...

  7. SPA项目开发登陆注册

    使用vue-cli脚手架工具创建一个vue项目 vue init webpack pro01 npm安装elementUI cd pro01 #进入新建项目的根目录 安装: npm install a ...

  8. MySQL学习——操作表里的数据

    MySQL学习——操作表里的数据 摘要:本文主要学习了使用DML语句操作表里数据的方法. 插入数据 语法 通过传入数据插入: insert into 表名 [(列名1, …, 列名n)] values ...

  9. 被 GANs 虐千百遍后,我总结出来的 10 条训练经验

    一年前,我决定开始探索生成式对抗网络(GANs).自从我对深度学习产生兴趣以来,我就一直对它们很着迷,主要是因为深度学习能做到很多不可置信的事情.当我想到人工智能的时候,GAN是我脑海中最先出现的一个 ...

  10. php 的定界符 <<<eof

    PHP是一个Web编程语言,在编程过程中难免会遇到用echo来输出大段的html和javascript脚本的情况,如果用传统的输出方法 ——按字符串输出的话,肯定要有大量的转义符来对字符串中的引号等特 ...