1.算法思想
(注意:本文章仅为个人笔记使用,仅保证程序能达到算法需要的效果,而不能担保程序的规范与简洁,请各位斟酌看待)
算法介绍
并查集是一种树型(数组形式表达)的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。
2.所需知识
【1】树的基础理解
【2】熟练掌握数组思想及运用
3.算法组成
【1】算法结构
(1)parent[n] 数组:通常把所有的节点写入一维数组,并初始化为-1
这么做相当于把每一个节点看作一颗仅有一个根节点的树
(2)rank[n] 数组:用于记录每一个集合(不同的树)的高度(根节点高度设为0)
初始化为0
【2】算法组成
(1)查找函数 int findRoot( int x , int parent[ ] )
查找某一个节点的根节点
//寻找根节点
int findRoot(int x,int parent[])
{
int x_root=x;
while(parent[x_root]!=-1){
x_root=parent[x_root];
}
return x_root;
}
若想判断两个节点是否属于同一颗树,直接调用该函数,查看根节点是否相同即可
(2)合并函数 int unionTree(int x , int y , int parent[ ] , int rank[ ] )
当两个点所处的集合相同时,返回0,表示不用合并
当两个点所处的集合不同时,把树的高度小的集合并到高度大的集合下面(若rank即树的度
太高,会使查找根节点的时间变长,因此需要尽可能减小树的高度)
int unionTree(int x,int y,int parent[],int rank[])
{
int x_root = findRoot( x ,parent ) , y_root = findRoot( y , parent );
if( x_root == y_root ){
return 0;
}else if( rank[x_root] > rank[y_root] ){
parent[y_root]=x_root;
}else if( rank[x_root] < rank[y_root] ){
parent[x_root]=y_root;
}else if( rank[x_root] == rank[y_root] ){//当两颗树高度相同时,随便一颗并入另一颗下面
parent[x_root]=y_root; //并使并入后的新树高度+1
rank[y_root]++;
}
return 1;
}
(3)统计有多少个不同的集合
直接遍历parent【n】数组,统计有多少个负数即可(-1代表根节点)
【3】全部代码
#include <iostream>
using namespace std;
//寻找根节点
int findRoot(int x,int parent[])
{
int x_root=x;
while(parent[x_root]!=-1){
x_root=parent[x_root];
}
return x_root;
}
//合并不同的集合??
int unionTree(int x,int y,int parent[],int rank[])
{
int x_root = findRoot( x ,parent ) , y_root = findRoot( y , parent );
if( x_root == y_root ){
return 0;
}else if( rank[x_root] > rank[y_root] ){
parent[y_root]=x_root;
}else if( rank[x_root] < rank[y_root] ){
parent[x_root]=y_root;
}else if( rank[x_root] == rank[y_root] ){//当两颗树高度相同时,随便一颗并入另一颗下面
parent[x_root]=y_root; //并使并入后的新树高度+1
rank[y_root]++;
}
return 1;
}
//统计集合的个数
int size(int parent[],int n)//n代表节点的个数
{
int cnt=0,i=0;
for(i=0;i<n;i++)
{
if(parent[i]<0)
cnt++;
}
return cnt;
}
int main()
{
//初始化数据
int n=9,i=0;
int parent[n],rank[n]={0};
for(i=0;i<n;i++)
parent[i]=-1;
//把最下面的测试数据输入
unionTree( 0 , 1 , parent , rank );
unionTree( 1 , 2 , parent , rank );
unionTree( 2 , 3 , parent , rank );
unionTree( 4 , 5 , parent , rank );
unionTree( 6 , 7 , parent , rank );
unionTree( 7 , 8 , parent , rank );
cout<<"共有 "<<size(parent,n)<<" 个集合\n";
cout<<"节点2和节点3属于同一个集合吗?\n";
if( findRoot( 2 , parent ) == findRoot( 3 , parent ) ){
cout<<"是\n";
}else{
cout<<"不是\n";
}
}
/*
测试数据,共3个集合{0,1,2,3},{4,5},{6,7,8}
*/
4.算法应用
图是否存在环?
介绍:判断一个图是否有环
代码附上:
//并查集(判断图存在环)
#include<iostream>
using namespace std;
/*
变量说明:
parent代表当前坐标的父节点,rank代表树的高度(从0开始)
*/
//寻找根节点
int findRoot(int x,int parent[])
{
int x_root=x;
while(parent[x_root]!=-1){
x_root=parent[x_root];
}
return x_root;
}
//合并两棵树,不成环返回1,成环返回0
int unionTree(int x,int y,int parent[],int rank[])
{
int x_root=findRoot(x,parent);
int y_root=findRoot(y,parent);
//当两个根节点相等时,说明两个节点实际上属于同一棵树,而树的节点之间有路径
//显然已经形成了一个环,直接退出,并返回0
if(x_root==y_root)
return 0;
if(rank[x_root]>rank[y_root]){
parent[y_root]=x_root;
}else if(rank[x_root]<rank[y_root]){
parent[x_root]=y_root;
}else{
parent[x_root]=y_root;
rank[y_root]++;
}
return 1;
}
/*
测试数据
数据1:6个节点,6条边,环状
6 6
0 1
1 2
2 4
1 3
3 5
4 5
数据2:6个节点,5条边,不成环
6 5
0 1
1 2
2 4
1 3
3 5
*/
int main()
{
int n,arc;
cout<<"请输入节点数目和边的数目:";
cin>>n>>arc;
int rank[n]={0},parent[n];
for(int i=0;i<n;i++)//父节点初始化为-1
parent[i]=-1;
cout<<"请输入顶点之间的关系:";
int vi,vj,graph[n][2];
for(int i=0;i<arc;i++)
{
cin>>vi>>vj;
graph[i][0]=vi;
graph[i][1]=vj;
}
int flag=0;//flag用于辅助标记环的存在,便于输出结果
for(int i=0;i<arc;i++)
{
int x=graph[i][0];
int y=graph[i][1];
if(unionTree(x,y,parent,rank)==0){
flag=1;
break;
}
}
if(!flag)
cout<<"没有环!\n";
else
cout<<"有环存在!\n";
}
如果有不对的地方希望大家能够在评论区指出,希望能和大家一起进步!!!
如果喜欢的话可以分享给小伙伴~