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. 你真的会玩SQL吗?表表达式,排名函数

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  2. Android ScrollView监听滑动到顶部和底部的两种方式(你可能不知道的细节)

    Android ScrollView监听滑动到顶部和底部,虽然网上很多资料都有说,但是不全,而且有些细节没说清楚 使用场景: 1. 做一些复杂动画的时候,需要动态判断当前的ScrollView是否滚动 ...

  3. UWP简单示例(一):快速合成音乐MV

    准备 IDE:Visual Studio 2015 为你的项目安装Nuget包 SharpDx.XAudio2 为你的项目安装Nuget包 Win2D.UWP 了解并学习:Win2D官方博客 了解并学 ...

  4. 数据上下文【 DnContext】【EF基础系列7】

    DBContext: As you have seen in the previous Create Entity Data Model section, EDM generates the Scho ...

  5. struts2类型转换

    1. Struts2中的类型转换 我们知道通过HTTP提交到后台的数据,都是字符串的形式,而我们需要的数据类型当然不只字符串类型一种.所以,我们需要类型转换! 在Struts2中,类型转换的概念除了用 ...

  6. JAVA大数据数组排序

    对于数据排序大家肯定见过不少,选择排序或者冒泡排序等等,今天我们要做的是快速排序 + 直接插入排序来对大数据(1000万以上)进行排序,下面我们分别来看看这两种排序规则 1, 直接插入排序 (1)基本 ...

  7. shiro在springmvc里面的集成使用【转】

    <dependency> <groupId>commons-collections</groupId> <artifactId>commons-coll ...

  8. Placeholder如何换行

    使用js动态添加标签充,处理换行问题 var placeholder = 'This is a line \nthis should be a new line'; $('textarea').att ...

  9. 前端导出Excel兼容写法

    今天整理出在Web前端导出Excel的写法,写了一个工具类,对各个浏览器进行了兼容. 首先,导出的数据来源可能有两种: 1. 页面的HTML内容(一般是table) 2. 纯数据 PS:不同的数据源, ...

  10. xml文件解析(解析以后在RootTableViewController输出)

    这是从美团弄得xml文件,地区和经纬度. 你点了地区以后 ,  就可以查看经纬度 ,因为笔者懒, 有现成的文本框 , 所有偷懒了. 下面是一些枯燥的代码了 . #import <UIKit/UI ...