并查集
树形的数据结构,每个集合有其代表节点,代表节点相同的元素属于同一集合。
find
:通过查找节点的代表节点,判断节点所属集合。
union
:合并两集合,小集合合并到大集合,使用大集合的代表节点。
class UnionFindSet {
private:
unordered_map<int, int> fatherMap;
unordered_map<int, int> nodesNum;
public:
UnionFindSet(vector<int> vec)
{//初始化时,每个节点各自一个集合
for (int i : vec)
{
fatherMap[i] = i;
nodesNum[i] = 1;
}
}
/*
查找代表节点(父节点是自身)
如果当前节点不是代表节点,就递归查找,直到找到。
在递归的回退过程中,令路过每个节点的父节点直接改为代表节点,
在下次查找该集合元素时可以提高查找速度,不用再递归。
*/
int find(int x)
{
if (fatherMap[x] == x)
return x;
else
return fatherMap[x] = find(fatherMap[x]);
}
//合并两个节点所在的集合,小集合合并到达集合上
void unionSet(int a, int b) {
int father_a = find(a);
int father_b = find(b);
if (father_a != father_b) {
int num_a = nodesNum[father_a];
int num_b = nodesNum[father_b];
if (num_a > num_b) {
fatherMap[father_b] = father_a;//大集合是爸爸
nodesNum[father_a] += num_b;
}
else {
fatherMap[father_a] = father_b;
nodesNum[father_b] += num_a;
}
}
}
};
在find
的递归过程中,让路过节点的父节点直接赋值为代表节点,节省下次查找时间,如图所示。
- 计算岛的个数
如果有一片1连在一起, 这个部分叫做一个岛, 求一个矩阵中有多少个岛?
举例:
0 0 1 0 1 0
1 1 1 0 1 0
1 0 0 1 0 0
0 0 0 0 0 0
这个矩阵中有三个岛
遍历二维数组,遇到1时就将所相连的1都改为2,看看遇到多少次1,就是岛的数量。改数时使用回溯(类似于走迷宫)。回溯的基本过程:
- 如果符合条件:
- 执行改动
- 递归向下一层
- 撤消改动
但是我们希望在递归的回退过程中,将执行的改动保留下来,因此不需要第三步。
void infect(vector<vector<int>>& mat, int i, int j)
{
int m = mat.size(), n = mat[0].size();
if (i >= 0 && i < m && j >= 0 && j < n && mat[i][j] == 1)
{
mat[i][j] = 2;
infect(mat, i - 1, j);
infect(mat, i + 1, j);
infect(mat, i, j - 1);
infect(mat, i, j + 1);
}
}
int isolationNum(vector<vector<int>> mat)
{
if (mat.empty() || mat[0].empty()) return 0;
int m = mat.size(), n = mat[0].size();
int res = 0;
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
if (mat[i][j] == 1) {
res++;
infect(mat, i, j);
}
}
}
return res;
}
当矩阵较大,可以通过并行计算分别计算每块的岛屿数量,然后再合并集合,没合并一次计数减一。