并查集
并查集
顾名思义,并查集分为合并和查询。在图中给定n点问你n个点组成多少部分,如:4 2 1 3 4 3
第一行告诉你,一共有4个点,2条路。下面两行告诉你,1、3之间有条路,4、3之间有条路。那么整幅图就被分成了1-3-4和2两部分。
合并的思想:
主要使用树型来维护在同一个树代表同一个部分,用个pre数组来表示没个点的父亲节点,一开始每个人父亲节点都是自己,但有两个点相连时候只要把其中一个的父亲节点设为另一个点,这样两个点在同一棵树上,也就表示同一部分。
伪代码:
void join(int root1, int root2) //判断是否连通,不连通就合并
{
int x, y;
x = unionsearch(root1); ///查询两点最高节点是否是同一个(查询的部分)
y = unionsearch(root2);
if(x != y) ///如果不连通,就把它们所在的连通分支合并
pre[x] = y;
}
查询的思想:
由于用树来建图,所以判断两个点是否已经联通只要判断他最高节点是否相同就可以了,而经过一次查询就可以把已经查过的点父亲节点改为最高节点,下次就不要一级一级往上查了
伪代码:
int unionsearch(int root) //查找根结点
{
int son, tmp;
son = root;
while(root != pre[root]) //寻找根结点
root = pre[root];
while(son != root) //路径压缩
{
tmp = pre[son];
pre[son] = root;
son = tmp;
}
return root;
}
外送两个递归代码
int Find (int x) /// 查询
{
if (pre[x] != x)
pre[x] = Find(pre[x]); ///用pre[x]等于可以直接压缩路径
return pre[x];
}
void join(int x,int y) ///合并
{
pre[Find(x)] = Find(y);
}
差不多就是这样了。不懂的推荐个博客可以看看:
挺好玩的【http://blog.youkuaiyun.com/niushuai666/article/details/6662911#comments】
练习传送门:
1. 【http://acm.hdu.edu.cn/showproblem.php?pid=1232】
2. 【http://poj.org/problem?id=2542】
3. 【http://poj.org/problem?id=1182】