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] Number of Islands II 岛屿的数量之二的更多相关文章

  1. [LeetCode] 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 oper ...

  2. [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 ...

  3. 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 ...

  4. 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 ...

  5. [LeetCode] Number of Islands II

    Problem Description: A 2d grid map of m rows and n columns is initially filled with water. We may pe ...

  6. [LeetCode] 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 ...

  7. [LeetCode] 685. Redundant Connection II 冗余的连接之二

    In this problem, a rooted tree is a directed graph such that, there is exactly one node (the root) f ...

  8. [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 ...

  9. LeetCode 305. Number of Islands II

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

随机推荐

  1. Linux 平台静默安装 Oracle客户端

    需求:Linux平台,安装完整版Oracle客户端 Tips:如果只是用到sqlldr,sqlplus功能,可以参考<Linux上oracle精简版客户端快速部署>快速部署精简版:如果需要 ...

  2. Vertica 高可用性测试

    1.基本概念介绍 2.停止某节点服务 3.测试其他节点访问 1.基本概念介绍 Vertica也是MPP架构的数据库,相比大家熟悉的MPP架构,比如Greenplum和hadoop这些产品,Vertic ...

  3. 在项目中同时使用Objective-C和Swift

    苹果发布的Swift语言可以和之前的Objective-C语言同时存在于一个项目中. 可能有人会认为是同一个类文件中既可以有Objective-C也可以有Swift,这是不对的.同一个类文件或同一个代 ...

  4. StackExchange.Redis客户端读写主从配置,以及哨兵配置。

    今天简单分享一下StackExchange.Redis客户端中配置主从分离以及哨兵的配置. 关于哨兵如果有不了解的朋友,可以看我之前的一篇分享,当然主从复制文章也可以找到.http://www.cnb ...

  5. C#开发微信门户及应用(13)-使用地理位置扩展相关应用

    本文继续上一篇<C#开发微信门户及应用(12)-使用语音处理>,继续介绍微信的相关应用.我们知道,地理位置信息可以用来做很多相关的应用,除了我们可以知道用户所在的位置,还可以关联出一些地理 ...

  6. Is-A,Has-A,Use-A(转载)

    原文地址:http://blog.csdn.net/loveyou128144/article/details/4749576 而Is-A,Has-A,Use-A则是用来描述类与类之间关系的.简单的说 ...

  7. nginx反向代理下thinkphp、php获取不到正确的外网ip

    在记录用户发送短信需要获取用户ip时,tp一直获取的是内网ip:10.10.10.10 tp框架获取ip方法:get_client_ip /** * 获取客户端IP地址 * @param intege ...

  8. 评《撸一段 SQL ? 还是撸一段代码? 》

    最近看到一篇博客<撸一段 SQL ? 还是撸一段代码?>,文章举例说明了一个连表查询使用程序code来写可读性可维护性更好,但是回帖意见不一致,我想作者在理论层面没有做出更好的论述,而我今 ...

  9. IE7,6与Fireofx的CSS兼容性处理方法集结

    CSS对浏览器的兼容性有时让人很头疼,尤其是对于IE6这个问题多多的浏览器版本,从网上收集了IE7,6与Fireofx的兼容性处理方法并整理了一下.对于web2.0的过度,请尽量用xhtml格式写代码 ...

  10. JavaScript学习笔记1之基础知识点

    一.什么是JavaScrip JavaScript是一种动态类型.弱类型.基于原型的客户端脚本语言.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,在HTML网页 ...