并查集(Disjoint Set)

并查集是什么

并查集是一种数据结构,用来检查一个图是否存在环

说明

在这里插入图片描述
在这里插入图片描述
把图上的点列出来,把图上所有的边过一遍(按照任意的顺序都行)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
到目前为止已经走过的边有:(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("不存在环");
    }
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值