并查集:合并、查询,把握效率!
每个集合选取一个主要领头元素,方便查找。
查找合并时间复杂度:O(m*α(n)), α函数是增长非常缓慢的函数。
优化:路径压缩+按秩合并(小的合并到大的组)
实现方式:
一、数组实现:当数据是整型标识且不是很大
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stack>
#define Max 10010
#define max(a,b) a>b?a:b;
#define min(a,b) a>b?b:a;
using namespace std;
struct Node{
int root;//父节点
int count;//节点数量
}a[Max];
int n,m;
stack<int> s1; //用来保存路径,方便后面修改
stack<int> s2; //有两个集合
//void show(int n) //检查
//{
// for(int i=1;i<n;i++)
// {
// printf("%d %d\n",a[i].root,a[i].count);
// if(i!=n-1)
// printf(" ");
// }
// printf("\n");
//}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) //初始化
{
a[i].count=1;
a[i].root=i;
}
int z,x,y;
while(m--)
{
scanf("%d%d%d",&z,&x,&y);
if(z==1) //合并
{
s1.push(x); //先默认把自己入栈
while(a[x].root!=x)
{
x=a[x].root;
s1.push(x);
}
s2.push(y);
while(a[y].root!=y)
{
y=a[y].root;
s2.push(y);
}
if(a[x].count>=a[y].count) //把小的合并到大的
{
a[x].count+=a[y].count;
while(s1.size())
{
a[s1.top()].root=x;
s1.pop();
}
while(s2.size())
{
a[s2.top()].root=x;
s2.pop();
}
}
else
{
a[y].count+=a[x].count;
while(s1.size())
{
a[s1.top()].root=y;
s1.pop();
}
while(s2.size())
{
a[s2.top()].root=y;
s2.pop();
}
}
}
else
{
s1.push(x); //先默认把自己入栈
while(a[x].root!=x)
{
x=a[x].root;
s1.push(x);
}
while(s1.size()) //不管输赢都更新,一遍下次方便查找
{
a[s1.top()].root=x;
s1.pop();
}
s2.push(y);
while(a[y].root!=y)
{
y=a[y].root;
s2.push(y);
}
while(s2.size())
{
a[s2.top()].root=y;
s2.pop();
}
if(a[x].root==a[y].root)
{
printf("Y\n");
}
else
printf("N\n");
}
}
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<string.h>
using namespace std;
int i,j,k,n,m,s,ans,f[10010],p1,p2,p3;
//f[i]表示i的集合名
int find(int k){
//路径压缩
if(f[k]==k)return k;
return f[k]=find(f[k]);
}
int main()
{
cin>>n>>m;
for(i=1;i<=n;i++)
f[i]=i;//初始化i的老大为自己
for(i=1;i<=m;i++){
cin>>p1>>p2>>p3;
if(p1==1)
f[find(p2)]=find(p3);
//p3打赢了p2
else
if(find(p2)==find(p3))
//是否是一伙的
printf("Y\n");
else
printf("N\n");
}
return 0;
}
二、链表实现:效率貌似不高
三、森林/树实现
思路同上,有类似的代码后期补上。