并查集的应用
上篇文章我们了解了并查集的内容,点这里看原文。而本文主要以leedcode题库中的两道题,来说下并查集的应用。
第一题:200.岛屿数量
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:[['1','1','1','1','0'],
['1','1','0','1','0'],
['1','1','0','0','0'],
['0','0','0','0','0']]
输出: 1
示例 2:
输入:[['1','1','0','0','0'],
['1','1','0','0','0'],
['0','0','1','0','0'],
['0','0','0','1','1']]
输出: 3
解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。
题目分析
此题目还有别的解法,这里我们只讨论并查集的解法。
这个问题跟 547.朋友圈(如没有看过朋友圈问题,可以查看并查集总结,有朋友圈问题的介绍)是有一些区别的。朋友圈问题的二维矩阵是主队对角线全为1的对称矩阵,横纵编号代表学生编号,且横纵编号对应相同的编号是同一个人,就是以一个个体人为节点使用UnionFind算法。
但这个岛屿问题的二维矩阵,横纵坐标更像是经纬度,横纵编号没有特定的相同之处,那么这个题目是否可以使用并查集求解呢?
答案是肯定的。
如果在这里我们以二维矩阵中的每一个元素作为一个节点,那么等价关系就可以是:如果这个节点值是“1”,与这个节点的上下左右相邻的节点值也是“1”这样的【等价关系】。这样考虑的等价关系,就可以使用并查集了。
如果还想不通,还记得上篇文章举的例子吗?
如果人步行就可以从一个岛屿走到另一个岛屿,我们就可以说这两个岛屿是连通的。那么这里我们把每个值是“1”的节点,想象成是独立的岛屿,而如果两个岛屿之间是上下左右相邻的话,就相当于在这两者之间建立了桥,这样就可以连通起来,视为一个岛屿。
是不是一下子就想到可以用并查集求解了呢?
这样一来,相较上篇文章,UnionFind类里一些方法就需要修正一下:
首先是初始化时的构造函数,
由于我们计划使用二维矩阵中每个元素作为节点,那么传入的参数可以直接使用二维矩阵grid[][]。
这里有个关键之处就是count,连通分量的计数器,在这里初始化的值应该是开始时“互相独立”的岛屿,即节点数为“1”的节点总数,是通过先设count=0;再遍历整个二维数组,如果节点值为“1”,就count加1,就正好是节点值为“1”的节点总数了。
还有一个点,就是parent数组,采用一维数组就可以记录某节点的父亲节点,采用二维位置与一维下标一一对应即可,这里使用 i * grid[0].length + j 做映射。
代码如下:
class UnionFind_2{
private int count;
private int[] parent;
private int[] size;
public UnionFind_2(char[][] grid){
int m = grid.length;
int n = grid[0].length;
this.count = 0;
parent = new int[m * n];
size = 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++;
}
size[i * n + j] = 1;
}
}
}
//...
}
UnionFind类中其他方法没有改变,如下
private int find(int p){
int x = p;
while (parent[x] != x){
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
public void