No.7.2 并查集算法
暂时没看懂,只做记录,后续再究!
一、求有几个强盗同伙?
现在有10个强盗:
1与2是同伙; 3与4是同伙;
5与2是同伙; 4与6是同伙;
2与6是同伙; 8与7是同伙;
9与7是同伙; 1与6是同伙;
2与4是同伙;
强盗同伙的同伙,也是同伙。请问共有几个独立的强盗同伙?
解题思路:
1.首先假设10个强盗各不相属,用一维数组表示 f[i] = i
2.合并同伙:如果两人是同伙,以靠左原则为准,比如:“5与2是同伙,左边是5,那么2从属于5” <=>
1). u,v ( f[u]=u, f[v]=v ), 靠左原则 f[v]=u;
2). w,v ( f[w]=w, f[v]=u ), "擒贼先擒王"原则,u,v 都要从属于 w, 即 f[v]=w; f[u]=w;
3). v, x ( f[v]=w, f[x]=x ), x 从属于 v,f[x] = f[v] = w;
上述过程即并查集算法:每个点开始都是只有一个节点的树,通过一些条件合并成一颗大树,合并过程要遵循靠左原则和擒贼先擒王原则,找到相同的父节点和根节点。
二、code
int t[100]={0},n,m;
//初始化,t[i]=i,表示每个点都是自己的父节点,相互之间没有关系
void init(){
int i;
for(i=1;i<=n;i++){
t[i]=i;
}
}
int getf(int v){ // 寻找根节点
if(t[v] == v)
return v;
else{
t[v]=getf(t[v]); // v 是一维数组的索引坐标,t[v] 才是其父节点,所以 t[v] = getf( t[v] ); 在递归过程中,中间节点的父节点也被更新
return t[v]; // 返回值是父节点,不是 v
}
}
/* 其实这个函数用一句就可以代替:t[ getf(y) ] = t[ getf(x) ];
void merge(int x,int y){
int t1,t2;
t1=getf(x);
t2=getf(y);
if(t1!=t2){
t[t2]=t1;
}
}
*/
int main(){
int i,a,b;
int num=0;
scanf("%d %d",&n,&m);
init();
for(i=1;i<=m;i++){
scanf("%d %d",&a,&b);
t[ getf(b) ] = t[ getf(a) ];
}
for(i=1;i<=n;i++)
printf("%d ",t[i]);
for(i=1;i<=n;i++){
if(t[i]==i) // 在假设的前提下,只有根节点的值不变,可以求出群的个数
num++;
}
printf("\n%d\n",num);
getchar();getchar();return 0;
}
三、代码不大,算法也不难,关键是怎么分解、实现。这是才是入门的基础!