并查集
并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。
并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。
主要操作
初始化
把每个点所在集合初始化为其自身。
通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N)。
查找
查找元素所在的集合,即根节点。
合并
将两个元素所在的集合合并为一个集合。
通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的“查找”操作实现。
路径压缩
路径压缩,就是在每次查找时,令查找路径上的每个节点都直接指向根节点
eg:HDU 1232(畅通工程)
#include<stdio.h>
#include<iostream>
#define MAX 10005
using namespace std;
int pre[1000];
void init(){//初始化
for(int i = 1; i <= 1000; ++i){
pre[i] = i; //将每个结点的前导点设置为自己
}
}
int find(int x){//查找
int r = x;
while(pre[r] != r)
r = pre[r]; //找到他的前导点
int i = x, j;
while(i != r){ //路径压缩算法,记录x的前导点,将i的前导点设置成为r的跟结点
j = pre[i];
pre[i] = r;
i = j;
}
return r;
}
void join(int x, int y){//合并
int a = find(x);//x的根结点为a,y的根结点为b
int b = find(y);//如果a,b不是相同的根结点,则说明a,b不连通
if(a != b){
pre[a] = b;//将a,b相连,将a的前导结点设置为b
}
}
int main(int argc, char const *argv[])
{
int N, M;
while(scanf("%d", &N) && N){
init();
int ans = 0;
scanf("%d", &M);
int a, b;
for(int i = 0; i < M; ++i){
scanf("%d%d", &a, &b);
join(a,b);
}
for(int i = 1; i <= N; ++i){
if(pre[i] == i){
ans++;
}
}
printf("%d\n", ans - 1);
}
}
ans - 1 所代表的是路径压缩之后,在连通的情况下,最后的一个结点一定等于自己本身
在非连通的状态下,结点等于自己本身就代表没有连通