A 2d grid map of m rows and n columns is initially filled with water. We may perform an addLand operation which turns the water at position (row, col) into a land. Given a list of positions to operate, count the number of islands after each addLand operation. 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.

Example:

Input: m = 3, n = 3, positions = [[0,0], [0,1], [1,2], [2,1]]
Output: [1,1,2,3]

Explanation:

Initially, the 2d grid grid is filled with water. (Assume 0 represents water and 1 represents land).

0 0 0
0 0 0
0 0 0

Operation 1: addLand(0, 0) turns the water at grid[0][0] into a land.

1 0 0
0 0 0 Number of islands = 1
0 0 0

Operation 2: addLand(0, 1) turns the water at grid[0][1] into a land.

1 1 0
0 0 0 Number of islands = 1
0 0 0

Operation 3: addLand(1, 2) turns the water at grid[1][2] into a land.

1 1 0
0 0 1 Number of islands = 2
0 0 0

Operation 4: addLand(2, 1) turns the water at grid[2][1] into a land.

1 1 0
0 0 1 Number of islands = 3
0 1 0

Follow up:

Can you do it in time complexity O(k log mn), where k is the length of the positions?

这道题是之前那道 Number of Islands 的拓展,难度增加了不少,因为这次是一个点一个点的增加,每增加一个点,都要统一一下现在总共的岛屿个数,最开始初始化时没有陆地,如下:

0 0 0
0 0 0
0 0 0

假如在(0, 0)的位置增加一个陆地,那么此时岛屿数量为1:

1 0 0
0 0 0
0 0 0

假如再在(0, 2)的位置增加一个陆地,那么此时岛屿数量为2:

1 0 1
0 0 0
0 0 0

假如再在(0, 1)的位置增加一个陆地,那么此时岛屿数量却又变为1:

1 1 1
0 0 0
0 0 0

假如再在(1, 1)的位置增加一个陆地,那么此时岛屿数量仍为1:

1 1 1
0 1 0
0 0 0

为了解决这种陆地之间会合并的情况,最好能够将每个陆地都标记出其属于哪个岛屿,这样就会方便统计岛屿个数。这种群组类问题,很适合使用联合查找 Union Find 来做,又叫并查集 Disjoint Set,LeetCode 中使用这种解法的题目还不少呢,比如 Friend CirclesGraph Valid TreeRedundant Connection II 等等。一般来说,UF 算法的思路是每个个体先初始化为不同的群组,然后遍历有关联的两个个体,如果发现其 getRoot 函数的返回值不同,则手动将二者加入一个群组,然后总群组数自减1。这里就要分别说一下 root 数组,和 getRoot 函数。两个同群组的个体,通过 getRoot 函数一定会返回相同的值,但是其在 root 数组中的值不一定相同,可以类比成 getRoot 函数返回的是祖先,如果两个人的祖先相同,那么其是属于一个家族的(这里不是指人类共同的祖先哈)。root 可以用数组或者 HashMap 来表示,如果个体是数字的话,那么数组就 OK,如果个体是字符串的话,可能就需要用 HashMap 了。root 数组的初始化可以有两种,可以均初始化为 -1,或者都初始化为不同的数字,博主一般喜欢初始化为不同的数字。getRoot 函数的写法也可用递归或者迭代的方式,可参见博主之前的帖子 Redundant Connection II 中的讨论部分。这么一说感觉 UF 算法的东西还蛮多的,啥时候博主写个 UF 总结贴吧。

那么具体来看这道题吧,此题跟经典的 UF 使用场景有一点点的区别,因为一般的场景中两个个体之间只有两种关系,属于一个群组或者不属于同一个群组,而这道题里面由于 water 的存在,就多了一种情况,只需要事先检测一下当前位置是不是岛屿就行了,总之问题不大。一般来说 root 数组都是使用一维数组,方便一些,那么这里就可以将二维数组 encode 为一维的,于是需要一个长度为 m*n 的一维数组来标记各个位置属于哪个岛屿,假设每个位置都是一个单独岛屿,岛屿编号可以用其坐标位置表示,但是初始化时将其都赋为 -1,这样方便知道哪些位置尚未变成岛屿。然后开始遍历陆地数组,若某个岛屿位置编码的 root 值不为 -1,说明这是一个重复出现的位置,不需要重新计算了,直接将 cnt 加入结果 res 中。否则将其岛屿编号设置为其坐标位置,然后岛屿计数加1,此时开始遍历其上下左右的位置,遇到越界或者岛屿标号为 -1 的情况直接跳过,现在知道初始化为 -1 的好处了吧,遇到是 water 的地方直接跳过。否则用 getRoot 来查找邻居位置的岛屿编号,同时也用 getRoot 来查找当前点的编号,这一步就是经典的 UF 算法的操作了,因为当前这两个 land 是相邻的,它们是属于一个岛屿,所以其 getRoot 函数的返回值 suppose 应该是相等的,但是如果返回值不同,说明需要合并岛屿,将两个返回值建立关联,并将岛屿计数 cnt 减1。当遍历完当前点的所有邻居时,该合并的都合并完了,将此时的岛屿计数 cnt 存入结果中,参见代码如下:

class Solution {
public:
vector<int> numIslands2(int m, int n, vector<vector<int>>& positions) {
vector<int> res;
int cnt = ;
vector<int> roots(m * n, -);
vector<vector<int>> dirs{{, -}, {-, }, {, }, {, }};
for (auto &pos : positions) {
int id = n * pos[] + pos[];
if (roots[id] != -) {
res.push_back(cnt);
continue;
}
roots[id] = id;
++cnt;
for (auto dir : dirs) {
int x = pos[] + dir[], y = pos[] + dir[], cur_id = n * x + y;
if (x < || x >= m || y < || y >= n || roots[cur_id] == -) continue;
int p = findRoot(roots, cur_id), q = findRoot(roots, id);
if (p != q) {
roots[p] = q;
--cnt;
}
}
res.push_back(cnt);
}
return res;
}
int findRoot(vector<int>& roots, int id) {
return (id == roots[id]) ? id : findRoot(roots, roots[id]);
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/305

类似题目:

Number of Islands

参考资料:

https://leetcode.com/problems/number-of-islands-ii/

https://leetcode.com/problems/number-of-islands-ii/discuss/75470/Easiest-Java-Solution-with-Explanations

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] 305. Number of Islands II 岛屿的数量之二的更多相关文章

  1. [LeetCode] 305. Number of Islands II 岛屿的数量 II

    A 2d grid map of m rows and n columns is initially filled with water. We may perform an addLand oper ...

  2. [LeetCode] Number of Islands II 岛屿的数量之二

    A 2d grid map of m rows and n columns is initially filled with water. We may perform an addLand oper ...

  3. LeetCode 305. Number of Islands II

    原题链接在这里:https://leetcode.com/problems/number-of-islands-ii/ 题目: A 2d grid map of m rows and n column ...

  4. 305. Number of Islands II

    题目: A 2d grid map of m rows and n columns is initially filled with water. We may perform an addLand  ...

  5. [LeetCode] 200. Number of Islands 岛屿的数量

    Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surro ...

  6. leetcode 200. Number of Islands 、694 Number of Distinct Islands 、695. Max Area of Island 、130. Surrounded Regions

    两种方式处理已经访问过的节点:一种是用visited存储已经访问过的1:另一种是通过改变原始数值的值,比如将1改成-1,这样小于等于0的都会停止. Number of Islands 用了第一种方式, ...

  7. [Swift]LeetCode305. 岛屿的个数 II $ Number of Islands II

    A 2d grid map of m rows and n columns is initially filled with water. We may perform an addLand oper ...

  8. Leetcode: Number of Islands II && Summary of Union Find

    A 2d grid map of m rows and n columns is initially filled with water. We may perform an addLand oper ...

  9. [leetcode]200. Number of Islands岛屿个数

    Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surro ...

随机推荐

  1. 送书『构建Apache Kafka流数据应用』和『小灰的算法之旅』和『Java并发编程的艺术』

    读书好处 1.可以使我们增长见识. 2.可提高我们的阅读能力和写作水平. 3.可以使我们变的有修养. 4.可以使我们找到好工作. 5.可以使我们在竞争激烈的社会立于不败之地. 6.最大的好处是可以让你 ...

  2. Docker 下开发安装hyperf

    Docker 下开发hyperf # 下载并运行 hyperf/hyperf 镜像,并将镜像内的项目目录绑定到宿主机的 /tmp/skeleton 目录 docker run -v /tmp/skel ...

  3. 一个动态构建 LambdaExpression Tree 的示例

    直接贴代码了: public class ExpressionTreeBuildingSampleTwo : Sample { public override string Name { get; } ...

  4. Dapper - 一款轻量级对象关系映射(ORM)组件,DotNet 下

    Dapper - a simple object mapper for .Net Official Github clone: https://github.com/SamSaffron/dapper ...

  5. 使用 jQuery.AutoComplete 让文本框自动完成

    直接贴代码了. @section headSection { <script type="text/javascript"> $(document).ready(fun ...

  6. LINQ 之 SelectMany

    声明:本文为www.cnc6.cn原创,转载时请注明出处,谢谢! 一.第一种用法: public static IEnumerable<TResult> SelectMany<TSo ...

  7. FIddler+Proxifer 实现PC客户端抓包

    Proxifier设置 1.安装Proxifier 下载地址:http://www.hanzify.org/software/13717.html 2.配置代理服务器 A.开启HTTP协议 Proxi ...

  8. 基于直接缓冲区和非直接缓冲区的javaIO文件操作

    基本概念: 1. 非直接缓冲区:  指的是通过jvm来缓存数据的,应用程序要读取本地数据要经历从本地磁盘到物理内存,然后copy到jvm中,然后再通过流的方式读取到应用程序中,写的操作正好与之相反. ...

  9. JVM 学习总结

    目录 Java内存区域 运行时数据区 & Java 内存结构 & Java 内存区域 1. 程序计数器 2. Java 虚拟机栈 3. 本地方法栈 4. 堆 5. 方法区 6. 运行时 ...

  10. jieba分词原理-DAG(NO HMM)

    最近公司在做一个推荐系统,让我给论坛上的帖子找关键字,当时给我说让我用jieba分词,我周末回去看了看,感觉不错,还学习了一下具体的原理 首先,通过正则表达式,将文章内容切分,形成一个句子数组,这个比 ...