先不要看我的整理,看一位大牛的文章,你就不用再看我写的了。
http://blog.csdn.net/dellaserss/article/details/7724401/
真的很厉害吧!用江湖来讲解一个算法,真的是绝妙的。我觉得学算法就是需要这样的生动有趣,很多人看见算法因为其专业的描述十分枯燥而变得十分头疼。如果能有生动形象的语言来描述它,就会令人兴趣大增。
它的思想就是:如果两个顶点a和b有一条路径相连,那么a的父顶点设为b,以此类推,b的父顶点c,如果连通,最后一个点的父顶点就是它本身。如果有其他顶点的父顶点是它本身,那么这样的图就不是连通的。
比如举个例子
输入边分别为:(0 , 1) , (0 , 2) , (0 , 3) , (1 , 3)
先初始化,每个点刚开始是独立为一个集合,它的父顶点就是自己。即pre[0] = 0 , pre[1] = 1 , pre[2] = 2 pre[3] = 3
接下来进行并查集:
- 查询0和1这两个点的父顶点,发现都为本身,返回父顶点 0和1,如果这两个父顶点不相等,则将两个集合合并,即pre[0] = 1,那么0的父节点就变成了1,这样0到1就有了一条路径。
- 查询0和2这两个点的父顶点,发现0的父顶点是1,而2的父顶点是本身2,则将两个集合合并,即pre[1] = 2,那么0和1的父顶点变成了2,这样1到2就有一条路径
- 查询0和3这两个点的父顶点,发现0的父顶点为2,而3的父顶点是本身,则将两个集合合并,即pre[2] = 3,那么0和1和2的父顶点变成了3,这样2到3就有一条路径。
- 查询1和3这两个点的父顶点,发现1的父顶点就是3,那么便不用合并集合了。
代码如下:
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1000+10;
int V,E;
int pre[maxn];
/*初始化*/
void init()
{
for(int i=1; i<=V; i++)
{
pre[i] = i;
}
}
/*查找根节点*/
int find(int x)
{
int r = x;
while(r!=pre[r])
r = pre[r];
int i = x;//路径压缩
int j;
while(i != r)
{
j = pre[i];
pre[i] = r;
i = j;
}
return r;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&V,&E);
init();
int s,t;
for(int i=0;i<E;i++)
{
scanf("%d%d",&s,&t);
int fx = find(s);
int fy = find(t);
if(fx != fy)
{
pre[fy] = fx;
}
}
int ok = 0;
for(int i=1; i<=V; i++)
{
if(pre[i]==i)
{
ok++;
}
}
if(ok==1)
printf("是连通图\n");
else{
printf("不是连通图\n");
}
}
return 0;
}
效果图: