💯 博客内容:并查集
😀 作 者:陈大大陈
🚀 个人简介:一个正在努力学技术的准C++后端工程师,专注基础和实战分享 ,欢迎私信!
💖 欢迎大家:这里是CSDN,我总结知识和写笔记的地方,喜欢的话请三连,有问题请私信 😘 😘 😘
目录
初版
路径压缩版
两个例题
几张桌子
是不是亲戚
并查集是一种挺实用的数据结构,可以处理一些不相交集合的合并问题。
基本操作有初始化,合并,查找,统计。
咱们先来个初版,再优化一下。
初版
#include<bits/stdc++.h>
using namespace std;
const int N = 10000;
int fa[N];
void init(int N)//初始化
{
for (int i = 1; i <= N; i++)
{
fa[i] = i;
}
}
int find(int i)//查找
{
return i == fa[i] ? i : find(fa[i]);
}
void Union(int i, int j)//合并
{
int x = find(i);
int y = find(j);
fa[x] = y;
}
这种并查集查找的效率太低,最坏情况的时间复杂度能达到O(N)。
我们就针对它优化。
路径压缩版
#include<bits/stdc++.h>
using namespace std;
const int N = 10000;
int fa[N];
void init(int N)
{
for (int i = 1; i <= N; i++)
{
fa[i] = i;
}
}
int find(int i)
{
if (i == fa[i])
{
return i;
}
else
{
fa[i] = find(fa[i]);
return fa[i];
}
}
void Union(int i, int j)
{
int x = find(i);
int y = find(j);
fa[x] = y;
}
上面的时间复杂度最好能有O(1)。
实现方式是记忆化搜索,用数组存储好所需结果,需要时就不用再次递归了。
来看两个例题巩固一下。
两个例题
几张桌子
How Many Tables(并查集)
Problem - 1213 (hdu.edu.cn)
题目:
Today is Ignatius’ birthday. He invites a lot of friends. Now it’s dinner time. Ignatius wants to know how many tables he needs at least. You have to notice that not all the friends know each other, and all the friends do not want to stay with strangers.
One important rule for this problem is that if I tell you A knows B, and B knows C, that means A, B, C know each other, so they can stay in one table.
For example: If I tell you A knows B, B knows C, and D knows E, so A, B, C can stay in one table, and D, E have to stay in the other one. So Ignatius needs 2 tables at least.
Input
The input starts with an integer T(1<=T<=25) which indicate the number of test cases. Then T test cases follow. Each test case starts with two integers N and M(1<=N,M<=1000). N indicates the number of friends, the friends are marked from 1 to N. Then M lines follow. Each line consists of two integers A and B(A!=B), that means friend A and friend B know each other. There will be a blank line between two cases.
Output
For each test case, just output how many tables Ignatius needs at least. Do NOT print any blanks.
Sample Input
2
5 3
1 2
2 3
4 5
5 1
2 5
Sample Output
2
4
思路:每个人是一个数组,两个数组内存的数如果相同,代表认识并成为一桌;(每个数组内刚开始都是自身编号)
给你们一组特殊测试数据:
输入:
1
5 4
1 2
1 3
4 3
3 4
输出:
2
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
const int N = 1050;
int fa[N];
void init()
{
for (int i = 1; i <= N; i++)
{
fa[i] = i;
}
}
int find(int i)
{
if (i == fa[i])
{
return i;
}
else
{
fa[i] = find(fa[i]);
return fa[i];
}
}
void Union(int i, int j)
{
int x = find(i);
int y = find(j);
fa[x] = y;
}
int main()
{
int t, x, y, n, m;
cin >> t;//t个测试
while (t--)
{
cin >> n >> m;
init();
for (int i = 1; i <= m; i++)
{
cin >> x >> y;
Union(x, y);//合并x和y
}
int ans = 0;
for (int i = 1; i <= n; i++)//统计有多少个集
{
if (fa[i] == i)
ans++;
}
cout << ans << endl;
}
return 0;
}
是不是亲戚
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
const int N = 1050;
int fa[N];
void init()
{
for (int i = 1; i <= N; i++)
{
fa[i] = i;
}
}
int find(int i)
{
if (i == fa[i])
{
return i;
}
else
{
fa[i] = find(fa[i]);
return fa[i];
}
}
void Union(int i, int j)
{
int x = find(i);
int y = find(j);
fa[x] = y;
}
int main()
{
int n, m, n2;;
cin >> n >> m;
init();
for (int i = 0; i < m; i++)
{
int x, y;
cin >> x >> y;
Union(x, y);
}
cin >> n2;
for (int i = 0; i < n2; i++)
{
int x, y;
cin >> x >> y;
if (find(x) == find(y))
cout << "Yes" << endl;
else
cout << "No" << endl;
}
return 0;
}