看图可以从这篇博客看,我不会画图。但是大体意思差不多
https://blog.csdn.net/u013486414/article/details/38682057
#include <iostream>
#include <algorithm>
#include <math.h>
using namespace std;
int a[10000];
int n;
int sz[1000];//从tunion3开始使用,标记没颗树的个数
int heigh[1000];//表示数的层数
void init(){
for(int i=0;i<n;i++){
//并查集最大的亮点就是当a[i]==i的时候表示是 根,以自身为根
a[i]=i;
sz[i]=1;//标记初始的时候,每棵树大小都是1
heigh[i]=1;//标记初始的时候,每棵树的层数都是1
}
}
int find1(int x){
return a[x];
}
void tunion1(int x,int y){
//找出根节点
int px=find1(x);
int py=find1(y);
if(px==py)
return;
for(int i=0;i<n;i++){//这个遍历的地方比较耗时
if(a[i]==px)
{
a[i]=py;
}
}
}
int find2(int xRoot){//这种方式,可能是某一个树太长,回溯找根浪费时间
while(xRoot!=a[xRoot])
{//不停的指向上直到找到自己的根节点
xRoot=a[xRoot];
}
return xRoot;//返回根节点
}
void tunion2(int x,int y){
int pxRoot=find2(x);
int pyRoot=find2(y);
if(pxRoot==pyRoot)
return;
//将以a[px]为根的树移动到以py为节点下
a[pxRoot]=pyRoot;
}
int find3(int xRoot){//这种方式,可能是某一个树太长,回溯找根浪费时间
while(xRoot!=a[xRoot])
{//不停的指向上直到找到自己的根节点
xRoot=a[xRoot];
}
return xRoot;//返回根节点
}
//将元素少的并入元素多的,可能会使层数变小
void tunion3(int x,int y){
int pxRoot=find3(x);
int pyRoot=find3(y);
if(pxRoot==pyRoot)
return;
//将以a[px]为根的树移动到以py为节点下
if(sz[pxRoot]<=sz[pyRoot]){
a[pxRoot]=pyRoot;
sz[pyRoot]+=sz[pxRoot];
}else{
a[pyRoot]=pxRoot;
sz[pxRoot]+=sz[pyRoot];
}
}
int find4(int xRoot){//这种方式,可能是某一个树太长,回溯找根浪费时间
while(xRoot!=a[xRoot])
{//不停的指向上直到找到自己的根节点
xRoot=a[xRoot];
}
return xRoot;//返回根节点
}
//将元素少的并入元素多的,可能会使层数变小
void tunion4(int x,int y){
int pxRoot=find4(x);
int pyRoot=find4(y);
if(pxRoot==pyRoot)
return;
//将以a[px]为根的树移动到以py为节点下,寻找父节点
if(heigh[pxRoot]<heigh[pyRoot]){
a[pxRoot]=pyRoot; //就算将一棵树移到另一颗树下,那么他的层数顶多和pyRoot的相同
}else if(heigh[pxRoot]>heigh[pyRoot]){
a[pyRoot]=pxRoot;//同理
}else{//heigh[pxRoot]==heigh[pyRoot]
a[pyRoot]=pxRoot;
heigh[pxRoot]++;
}
}
//路径压缩,但是这一种,是一层一层的网父亲的父亲上移动
int find5(int x){
while(x!=a[x]){
a[x]=a[a[x]];
x=a[x];
}
return x;
}
//递归版的路径压缩,压缩成每个树的层数就只有2层
int find5digui(int x){
if(x!=a[x]){
a[x]=find5digui(a[x]);
}
return a[x];
}
//非递归下的路径压缩,树的层数还是必须得要的
int find5feidigui(int x){
int k,j,r;
r=x;
while(r!=a[r]){
r=a[r];//找出根节点
}
k=x;
while(k!=r){ // 非递归路径压缩,从x开始的每一个parent节点都指向根节点
j=a[k]; // j暂存父节点
a[k]=r;// 父节点指向根节点
k=j;// k移到父节点
}
return r; //返回根节点的值
}
void tunion5(int x,int y){
int pxRoot=find5(x);
int pyRoot=find5(y);
if(pxRoot==pyRoot)
return;
//将以a[px]为根的树移动到以py为节点下,寻找父节点
if(heigh[pxRoot]<heigh[pyRoot]){
a[pxRoot]=pyRoot; //就算将一棵树移到另一颗树下,那么他的层数顶多和pyRoot的相同
}else if(heigh[pxRoot]>heigh[pyRoot]){
a[pyRoot]=pxRoot;//同理
}else{//heigh[pxRoot]==heigh[pyRoot]
a[pyRoot]=pxRoot;
heigh[pxRoot]++;
}
}
/**
*
*并查集的五种形式。从简单到路径压缩 用例0 1 2 3 4
* 1 2 ,1 3, 3 4 三组样例测试
*/
int main()
{
while(cin>>n){
if(n==0) break;
//memset(a,sizeof(a),0);
init();
int x,y,m;
cin>>m;
while(m--){//这个地方是设置那两个点是连接的
cin>>x>>y;
//tunion1(x,y);//quick find
//tunion2(x,y);//这个是随便构成一个树,无限层的连接
//tunion3(x,y);//这个是看元素的个数,谁的元素少那就并向谁
//tunion4(x,y);//这个是看层数,谁的层数小就并上谁
tunion5(x,y);//路径压缩
}
int ans=0;
for(int i=0;i<n;i++){
cout<<a[i]<<" "<<endl;
if(a[i]==i)
ans++;
}
cout<<endl;
//有几棵树
cout<<ans<<endl;
//有几条线就能联通
cout<<ans-1<<endl;
}
}