并查集
一、并查集的定义
二、并查集的三个基本操作
三、实例分析
1.三个基本操作模板
/*
-------并查集数据结构的实现------
1.makeSet
2.find
3.unionSet
*/
using namespace std;
const int MAXSIZE = 500;
int uset[MAXSIZE];
void makeSet(int size)
{
for (int i = 0; i < size; i++) uset[i] = -1;
}
//递归实现
int find_recur(int x)
{
if (uset[x] < 0) return x;
uset[x] = find_recur(uset[x]);
return uset[x];
}
//非递归实现
int find(int x)
{
int p = x, t;
while (uset[p] >= 0) p = uset[p];
while (x != p) {
t = uset[x];
uset[x] = p;
x = t;
}
return x;
}
void unionSet(int x, int y)
{
if ((x = find(x)) == (y = find(y))) return;
if (uset[x] < uset[y]) {
uset[x] += uset[y];
uset[y] = x;
}
else {
uset[y] += uset[x];
uset[x] = y;
}
} (1)亲戚
void test_unionSet01()
{
int n, m, p;
cin >> n >> m >> p;
//初始化并查集
makeSet(n);
//根据亲戚关系来合并相应的集合
while (m--)
{
int a, b;
cin >> a >> b;
unionSet(a, b);
}
//查找是否属于一个集合
while (p--)
{
int a, b;
cin >> a >> b;
if (find(a) == find(b))
cout << "YES" << endl;
else
cout << "NO" << endl;
}
} (2) 修路
int father[1005];
int Find(int x)
{
while (x != father[x])
x = father[x];
return x;
}
void Combine(int a, int b)
{
int fa = Find(a);
int fb = Find(b);
if (fa != fb)
{
father[fa] = fb;
}
}
void test_Unioset02()
{
int n, m;
int i;
int a, b;
while (~scanf("%d", &n))
{
if (n == 0)
break;
scanf("%d", &m);
int sum = 0;
for (i = 1; i <= n; i++)//初始化并查集
father[i] = i;
for (i = 0; i<m; i++)//合并
{
scanf("%d%d", &a, &b);
Combine(a, b);
}
for (i = 1; i <= n; i++)
{
if (father[i] == i)//统计没有被合并的结点个数
sum++;
}
printf("%d\n", sum - 1);//sum个结点之间最少还需要sum - 1条道路
}
} (3)数码宝贝
const int N = 100;
int a, b, m, n; //m, n分别为组数和个数 a, b表示每次在同一个集合的两个数
int father[N]; //集
//在合并之前初始化每个节点为该集合的根节点(每个数都是一个集合)
void init(int n) {
for (int i = 1; i <= n; i++) father[i] = i;
}
//找到元素x在该集合中的根节点(根节点满足x == father[x])
int findFather(int x) { //查
int a = x; //记录初始节点
while (x != father[x]) x = father[x]; //循环结束后的x为该集合的根节点
//路径压缩,将寻找根节点路途中的每个节点指向其根节点,优化之后的查找速度,可不写
while (a != father[a]) {
int b = a; //记录a,防止后面被覆盖
a = father[a]; //让a指向其父节点
father[b] = x; //指向根节点
}
return x;
}
//将a b合并到一个集合中
void Union(int a, int b) { //并
//分别找到a, b所在结合的根节点
int Ra = findFather(a);
int Rb = findFather(b);
//当a, b不在同一个集合中时将Ra指向Rb或者将Rb指向Rb指向ba
if (Ra != Rb) {
father[Ra] = Rb; //将Ra指向Rb
//father[Rb] = Ra; 也可以
}
}
void UnionSet03()
{
cin >> n >> m;
init(n); //在合并集合之前需初始化,勿遗忘
while (m--) {
cin >> a >> b;
Union(a, b); //每输入两个数,将其合并到一个集合中
}
int ans = 0; //用来记录集合的个数
//遍历所有数,根节点的数量即为集合的数量
for (int i = 1; i <= n; i++) {
if (i == findFather(i)) ans++; //i为其所在结合的根节点
}
cout << ans << endl;
}
int n, m;
int s, maxm;
int p[100002];
struct node {
int u;
int v;
int c;
}info[100002];
bool cmp(node x1, node x2)
{
if (x1.c != x2.c)return x1.c<x2.c;
//else if (x1.u != x2.u) return x1.u<x2.u;
//else return x1.v<x2.v;
}
int find(int x) //查找x元素所在的集合,回溯时压缩路径
{
if (x != p[x])
{
p[x] = find(p[x]);
}
return p[x];
}
void bcj(int x1, int x2)//把x2并入x1的集合
{
int t1, t2;//存储祖先节点
t1 = find(x1);
t2 = find(x2);
if (t1 != t2)p[t2] = t1;
}
void test_Unioset04()
{
cin >> n >> m;//n就是顶点数,m是边数
for (int i = 1; i <= n; i++)//初始化并查集
{
p[i] = i;
}
for (int i = 0; i<m; i++)
{
cin >> info[i].u >> info[i].v >> info[i].c;
}
sort(info, info + m, cmp);
for (int i = 0; i<m; i++)//遍历所有的边
{
if (find(info[i].u) != find(info[i].v))
{
bcj(info[i].u, info[i].v);//把v并入u的集合
maxm = max(maxm, info[i].c);
}
}
cout << n - 1 << " " << maxm;
} 
