无向图连通分量数量:
#include <cstdio>
using namespace std;
#define MAX 1000
int father[MAX]; //指向爸爸的树的表示法
//初始化,全部置-1,(-1表示这是一个根)
void initialize(int n){
for(int i = 0; i < n; i++)
father[i] = -1;
}
int find(int x){
if(father[x] == -1)
return x;
else return find(father[x]);
}
void unite(int x, int y){
int father_x = find(x);
int father_y = find(y);
if(father_x == father_y) return;
else {
father[father_x]=y;
}
}
int main(){
int v = 8;
int graph[8][8]={ //随便搞个2个连通分量的图
0,1,1,0,0,0,0,0,
1,0,0,0,0,0,0,0,
1,0,0,1,0,0,0,0,
0,0,1,0,1,0,0,0,
0,0,0,0,1,0,0,0,
0,0,0,0,0,0,1,1,
0,0,0,0,0,1,0,1,
0,0,0,0,0,1,1,0
};
initialize(v);
for(int i=0;i<v;i++){
for(int j=0;j<v;j++){
if(graph[i][j]==1)
unite(i,j);
}
}
int count =0;
for(int i=0;i<v;i++){
if(find(i) == i) count++; //如果根节点是自己,则表明找到了一个集合
}
printf("%d\n", count);
}
是否有环?
(两个点已经在一个集合里了结果又冒出来一条边,就有环)
int main(){
int v = 8;
int graph[8][8]={ //无环的
0,1,1,0,0,0,0,0,
1,0,0,0,0,0,0,0,
1,0,0,1,0,0,0,0,
0,0,1,0,1,0,0,0,
0,0,0,0,1,0,0,0,
0,0,0,0,0,0,0,1,
0,0,0,0,0,0,0,1,
0,0,0,0,0,1,1,0
};
initialize(v);
//无向图只遍历上三角!!
for(int i=0;i<v;i++){
for(int j=i+1;j<v;j++){
if(graph[i][j]==1){
int father_i = find(i);
int father_j = find(j);
if( father_i != father_j){
unite(i,j);
}
else {
printf("有环\n");
return 1;
};
}
}
}
printf("无环\n");
return 0;
}
优化:压缩路径
- 查的时候,将叶子变成儿子
- 并的时候,元素少的的并到元素多的的
- 如何记录元素数量?根结点原本用-1表示,可以改成-n。也就是用根的绝对值表示元素数量。
int find(int x){
int father_x = x;
while(father[father_x] >=0 ){
father_x = father[x];
}
father[x]=father[father_x]; //路径压缩,把元素直接挂在根结点下面那层
return father_x;
}
void unite(int x, int y){
int father_x = find(x);
int father_y = find(y);
if(father_x == father_y) return;
else {
if(father_x<father_y){ //x的那个集合元素更多,将元素较少的y的集合并到x的集合
father[father_x]+=father[father_y]; //修改元素数量
father[father_y]=father_x;
}else{
father[father_y]+=father[father_x];
father[father_x]=father_y;
}
}
}