并查集是:擒贼先擒王原则和靠左原则,先有一个找祖先的函数,找祖先就需要递,当找到后就需要归,在归的过程中加上返回值就会把路程中的小子孙们都赋上祖先的值,最后把祖先的值输出。然后再根据靠左原则,进行合并
#include <stdio.h>
#include <stdlib.h>
int f[1000]={0},n,m,k,sum=0;
void init()
{
int i;
for(i=1;i<=n;i++)
{
f[i]=i;
}
return ;
}
int getf(int v)
{
if(f[v]==v)
return v;
else
{
f[v]=getf(f[v]);//找祖先,一直找祖先,直到找到为止,这是递,归的时候因为有返回值,所以每次都会把返回值赋给上一层,最后在祖先旗下的所有孩子都会拥有祖先的值,并且最后输出的时候输出的也是祖先的值。
return f[v];
}
}
void merge(int v,int u)
{
int t1,t2;
t1=getf(v);
t2=getf(u);
if(t1!=t2)
f[t2]=t1;
return ;
}
int main()
{
int i,x,y;
scanf("%d%d",&n,&m);
init();
for(i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
merge(x,y);
}
for(i=1;i<=n;i++)
{
if(f[i]==i)sum++;
}
printf("%d\n",sum);
return 0;
}
小结:进一步明白了递归,递归就是先递后归,在递的过程中一层层的往下走,同时所有的步骤都会在每一层中出现,到递结束后就是归了,归的话是先从最后一层把所有的函数执行完,该返回的就返回,然后回到上一层,继续依次执行这个这一层的每一个函数;就这个函数来举个栗子:
int getf(int v)
{
if(f[v]==v)
return v;
else
{
f[v]=getf(f[v]);
return f[v];
}
}
当说2号和6号是同伙的时候,此时2号的老大是1号,1号的老大是5号,5号的老大是5号,先执行2号再来说6号,从2号开始进,不满足f[v]==v到达else,开始从1开始进,此时1号这一层有2个步骤,f[v]=getf(f[v]);
,1号也不满足,那就从5号开始进,也有2个步骤
return f[v];f[v]=getf(f[v]);
5号满足了,返回5号的值5,退回到上一层1的,把5赋值给f[1],并执行return f[1]=5,然后再退回到当一层2,把f[1]的值赋值给f[2],执行下一部返回f[2]的值5,这样就符合了在返回过程中把小子孙拥有的值改为祖先的值,其实是在归的过程实现的。
return f[v];
样例:
10 9
1 2
3 4
5 2
4 6
2 6
8 7
9 7
1 6
2 4
输出值为3