并查集
作用
并查集能够解决图论中的动态连通性的问题
什么是动态连通性满足下面三个特点
- 自反性 节点p 节点q连通
- 对称性 如果节点p节点q连通那么节点 q和节点p也连通
- 传递性 节点a 节点b连通 节点b 节点c连通 那么 节点a 节点c连通
原理
其实原理也是很简单的
-
首先维护一个
parent[]的数组,数组的含义就是parent[i]代表 i的父节点是parent[i] -
初始化的时候大家都各自为政,所有人都是自己的父节点
parent[i]=-1 -
当发现有一条边的时候,进行连接
parent[x]=y:将x挂在y上面 -
如果有两个区域要进行连接只需要找到他们对应的老大进行连接就可以,因为上面的传递性
实现
class Union_Find {
public:
Union_Find() {}
explicit Union_Find(int size) : size_(size), parent_(size, -1) {}
// return 1-链接成功 0-链接失败,有环
int Union(int x, int y) { // 作用就是连接 x y这两个节点
int parent_x = Find(x);
int parent_y = Find(y);
if (parent_x == parent_y) {
cout << "Have Cycle" << endl;
return 0;
}
parent_[x] = parent_y;
cout << "Sucess" << endl;
return 1;
}
int Find(int x) { // 寻找x节点的父节点
while (parent_[x] != -1) {
x = parent_[x];
}
return x;
}
private:
int size_; // 节点数
vector<int> parent_; // 记录父节点 i的父节点就是parent[i]
};
优化
为了防止随便的添加,我们维护一个数组保存子树下面的个数,每次连接的时候都把小的连到大的下面
class Union_Find {
public:
Union_Find() {}
explicit Union_Find(int size)
: size_(size), count_(size_), parent_(size, -1), weight_(1) {}
// return 1-链接成功 0-链接失败,有环
int Union(int x, int y) { // 作用就是连接 x y这两个节点
int parent_x = Find(x);
int parent_y = Find(y);
if (parent_x == parent_y) {
cout << "Have Cycle" << endl;
return 0;
}
if (weight_[parent_x] > weight_[parent_y]) {
parent_[parent_y] = parent_x;
weight_[parent_x] += weight_[parent_y];
} else {
parent_[parent_x] = parent_y;
weight_[parent_y] += weight_[parent_x];
}
cout << "Sucess" << endl;
count_--;
return 1;
}
int Find(int x) { // 寻找x节点的父节点
while (parent_[x] != -1) {
x = parent_[x];
}
return x;
}
int GetCount() { return count_; }
private:
int size_; // 节点数
vector<int> parent_; // 记录父节点 i的父节点就是parent[i]
vector<int> weight_; // 记录每一棵树的节点数量(重量)
int count_; // 记录连通分量的个数
};
路径压缩
就是其实把树的高度维持在3
#include <bits/stdc++.h>
using namespace std;
class Union_Find {
public:
Union_Find() {}
explicit Union_Find(int size)
: size_(size), count_(size), parent_(size), weight_(size, 1) {
for (int i = 0; i < size_; i++) {
parent_[i] = i;
}
}
// return 1-链接成功 0-链接失败,有环
int Union(int x, int y) { // 作用就是连接 x y这两个节点
int parent_x = Find(x);
int parent_y = Find(y);
if (parent_x == parent_y) {
cout << "Have Cycle" << endl;
return 0;
}
if (weight_[parent_x] > weight_[parent_y]) {
parent_[parent_y] = parent_x;
weight_[parent_x] += weight_[parent_y];
} else {
parent_[parent_x] = parent_y;
weight_[parent_y] += weight_[parent_x];
}
cout << "Sucess" << endl;
count_--;
return 1;
}
int Find(int x) { // 寻找x节点的父节点
while (parent_[x] != x) {
// 路劲压缩
// 条件表明当前节点有父节点,不是自己
parent_[x] = parent_[parent_[x]]; // 把自己指向爷爷
x = parent_[x];
}
return x;
}
int GetCount() { return count_; }
private:
int size_; // 节点数
vector<int> parent_; // 记录父节点 i的父节点就是parent[i]
vector<int> weight_; // 记录每一棵树的节点数量(重量)
int count_; // 记录连通分量的个数
};
int main() {
Union_Find instance(4);
instance.Union(0, 1);
// instance.Union(2, 2);
instance.Union(2, 3);
instance.Union(3, 2);
cout << instance.GetCount() << endl;
}
如果不初始化父节点是自己,那么路径压缩会出现内存泄露
valgrind进行查看,编译的时候请加入-g参数
==594== Invalid write of size 4
==594== at 0x400F84: Union_Find::Find(int) (union_find.cc:36)
==594== by 0x400DA5: Union_Find::Union(int, int) (union_find.cc:13)
==594== by 0x400BA0: main (union_find.cc:57)
==594== Address 0x5ab6c7c is 4 bytes before a block of size 16 alloc'd
==594== at 0x4C2E0EF: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==594== by 0x4015A1: __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*) (new_allocator.h:104)
==594== by 0x401532: std::allocator_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) (alloc_traits.h:491)
==594== by 0x40149D: std::_Vector_base<int, std::allocator<int> >::_M_allocate(unsigned long) (stl_vector.h:170)
==594== by 0x4013A8: std::_Vector_base<int, std::allocator<int> >::_M_create_storage(unsigned long) (stl_vector.h:185)
==594== by 0x401274: std::_Vector_base<int, std::allocator<int> >::_Vector_base(unsigned long, std::allocator<int> const&) (stl_vector.h:136)
==594== by 0x4010FD: std::vector<int, std::allocator<int> >::vector(unsigned long, int const&, std::allocator<int> const&) (stl_vector.h:291)
==594== by 0x400CB9: Union_Find::Union_Find(int) (in /home/zhongsy/Documents/算法代码/图算法代码/a.out)
==594== by 0x400B5E: main (union_find.cc:53)

本文深入介绍了并查集的基本概念、作用原理及实现方法,并详细探讨了如何通过优化提高其效率,包括路径压缩技术的应用。

被折叠的 条评论
为什么被折叠?



