并查集是什么
并查集是一种数据结构,用来检查一个图是否存在环
说明
把图上的点列出来,把图上所有的边过一遍(按照任意的顺序都行)
到目前为止已经走过的边有:(0,1)、(1,2) 、(1,3)、(3,4)
如果再选择其余的边的两个点均在同一个集合里(已选点)出现,就会成环。比如(2,4)
如何用代码实现
思路
不容易处理的点有
- 如何把两个集合合并
这里的思路是,用树的形式保存集合,用一维数组来构建树结构,初始化为-1,-1表示为独立的点
这样表示1的父节点为0
接下来就开是选图中的边了
如果下一次选边为1,3边,那么两个集合就要合并了。那么树结构怎么合并呢,如果用1指向3,结构就会太长,如果2指向3,结构就会断层。我们的想法是,把所有的父节点连接起来,1指向4或者4指向1,这里先用1指向4
如果下一次选择的是2-4边,沿着2往上找,找到2的根为4,4的根也是4,如果这两个根都是同一个,说明这两个点在同一个集合内,此时就能检测到有环了,剩下的一个边也没必须要探测了。
代码
public class DisJointSet {
static int vertices=6;//设置最大顶点数量
public static void main(String[] args) {
int[] parent = new int[vertices];
int[][] edges ={{0,1},{0,2},{2,3},{2,4},{2,5}};
Arrays.fill(parent, -1);//初始化
System.out.println(Arrays.toString(parent));
for(int i=0;i<edges.length;i++) {
int x = edges[i][0];
int y = edges[i][1];
if(!unionVertices(x, y, parent)) {
System.out.println("有环!");
return;
}
}
System.out.println("没有环!");
}
//找到x的根节点
static int findRoot(int x,int[]parent) {
int xRoot = x;
while(parent[xRoot]!=-1) {
xRoot=parent[xRoot];
}
return xRoot;
}
static boolean unionVertices(int x,int y,int[]parent){
int xRoot = findRoot(x, parent);
int yRoot = findRoot(y, parent);
if(xRoot==yRoot)return false;//如果在同一个集合里,不合并返回false
parent[xRoot]=yRoot;//根节点不是同一个,合并
return true;
}
}
优化:压缩
以上代码可能出现的问题就是如果图是链状,效率可能会降低。
思路是新建立一个数组保存数的深度。
package com.atguigu.disjointSet;
public class djset {
public static int VERTICES=6;
public static void initialise(int parent[]){
int i;
for (i = 0; i < VERTICES; i++) {
parent[i]=-1;
}
}
public static int find_root(int x,int parent[]){
int x_root=x;
while (parent[x_root]!=-1){
x_root=parent[x_root];
}
//出了循环说明已经找到父节点
return x_root;
}
/*
返回1表示union成功 合并成功,0表示合并失败
*/
public static int union_vertices(int x,int y,int parent[],int rank[]){
int x_root=find_root(x,parent);
int y_root=find_root(y,parent);
if(x_root==y_root){//x,y的父节点相同
return 0;
}else {
if(rank[x_root]>rank[y_root]){
parent[y_root]=x_root;
}else if(rank[y_root]>rank[x_root]){
parent[x_root]=y_root;
}else {
parent[x_root]=y_root;
rank[y_root]++;
}
}
}
public static void main(String[] args) {
int parent[]=new int[VERTICES];
int rank[]=new int[VERTICES];
int edges[][]={{0,1},{1,2},{1,3},
{2,4},
{3,4},{2,5}};
initialise(parent);
int i;
for ( i = 0; i < edges.length; i++) {
int x=edges[i][0];
int y=edges[i][1];
if(union_vertices(x,y,parent,rank)==0){
System.out.println("存在环");
return;
}
}
System.out.println("不存在环");
}
}