Disjoint Set (Union-Find )
codeflysafe Lv5

并查集数据结构

只有 findunion 操作, 它用于处理一些不交集的 合并查询 问题。

find : 确定元素所在的集合

union: 合并两个不相交集合

并查集的数据结构

初始化

初始化时,每个节点都是一个集合。

union(x,y)

合并的过程是: 先找到x(y)节点的祖先节点(find(x)) [xp,yp] ,然后比较祖先节点是否相同,若相同则完成合并。负责将其中的祖先节点 (xp) 的父节点指向另一个祖先节点 (yp):parent[xp] = yp;


一个简单的并查集模版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int parent[MAXLEN];

// 初始化通常将其自身作为父节点
void makeSet(int k){
for(int i =0;i<k;i++) parent[i] = i;
}

// 查找其根节点
int find(int x){
if(parent[x] != x){
parent[x] = find(parent[x]);
}
return parent[x];
}

// 合并两个不相交集合
void union(int x,int y){
int xp = find(x);
int yp = find(y);
if(xp != yp){
parent[xp] = yp;
}
while(parent[x]!=yp){
int t = parent[x];
parent[x]=yp;
x = t;
}
}

路径压缩

这样的确可以达成目的,但是显然效率实在太低。为什么呢?因为我们使用了太多没用的信息,我的祖先是谁与我父亲是谁没什么关系,这样一层一层找太浪费时间,不如我直接当祖先的儿子,问一次就可以出结果了。甚至祖先是谁都无所谓,只要这个人可以代表我们家族就能得到想要的效果。把在路径上的每个节点都直接连接到根上,这就是路径压缩。

https://oi-wiki.org/ds/images/disjoint-set-compress.svg

并查集的应用

1. 统计不相交集合的个数

200. 岛屿数量

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外,你可以假设该网格的四条边均被水包围。

示例 1:

输入:grid = [ ["1","1","1","1","0"], ["1","1","0","1","0"], ["1","1","0","0","0"], ["0","0","0","0","0"] ] 输出:1

示例 2:

输入:grid = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","1"] ] 输出:3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include<iostream>
#include<vector>
#include<string>

using namespace std;

class Solution {
public:

struct Node{
int x;
int y;
Node():x(0),y(0){};
Node(int x, int y):x(x),y(y){};
bool equal(Node node){return node.x == x && node.y == y;};
};



Node find(vector<vector<Node>> &parent, Node node){
while(!node.equal(parent[node.x][node.y])){
node = parent[node.x][node.y];
}
return node;
}

bool isParent(vector<vector<Node>> &parent, Node node){
return node.equal(parent[node.x][node.y]);
}

void unionNode(vector<vector<Node>> &parent, Node x, Node y){
Node xp = find(parent,x);
Node yp = find(parent,y);
if(!xp.equal(yp)) {
parent[xp.x][xp.y] = yp;
}
while(!x.equal(yp)){
Node node = parent[x.x][x.y];
parent[x.x][x.y] = yp;
x = node;
}
}

int numIslands(vector<vector<char>>& grid) {

int m = grid.size(), n = grid[0].size();
vector<vector<Node>> parent(m,vector<Node>(n,Node()));

for(int i =0;i<m;i++){
for(int j=0;j<n;j++){
parent[i][j] = Node(i,j);
}
}

vector<int> xd = {-1,0,1,0};
vector<int> yd = {0,1,0,-1};

for(int i =0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j] == '0') continue;
for(int k =0;k< 4;k++){
int x = xd[k] + i;
int y = yd[k] + j;
if(x >=0 && x < m && y >=0 && y < n){
if(grid[x][y] == '1') unionNode(parent,Node(i,j),Node(x,y));
}
}
}
}

int ans = 0;
for(int i =0;i<m;i++){
for(int j = 0;j<n;j++){
if(grid[i][j] == '1' && isParent(parent,Node(i,j))) ans++;
}
}
return ans;
}
};

Reference

https://oi-wiki.org/ds/dsu/

https://leetcode-cn.com/problems/number-of-provinces/solution/jie-zhe-ge-wen-ti-ke-pu-yi-xia-bing-cha-0unne/

  • 本文标题:Disjoint Set (Union-Find )
  • 本文作者:codeflysafe
  • 创建时间:2022-01-12 16:14:19
  • 本文链接:https://codeflysafe.github.io/2022/01/12/Disjoint S 8c71a/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论