Java实现_算法_并查集

并查集

作用:用来查找某个图中是否含有闭环。

比如图一:
在这里插入图片描述

上图中就是没有闭环的一个图,而下图(图二)就是一个有闭环的图
在这里插入图片描述

思路1-数组寻根法:

顾名思义,数组寻根法(自己称呼的)就是寻找每个节点的根节点,然后判断根节点是否相同。
具体的做法如下:
1.遍历每一条路径,然后根据路径去寻找根节点
2.如果有两个节点的根节点是相同的,那么就是可以组成闭环,如果到遍历完毕所有的路径之后还是没有相同根节点的两个点,说明这些点中不能够组成闭环
我们来用并查集的思想走一下图一
(1)首先创建拥有节点个数的整形数组arr,用来存放各个节点的根节点,在此我们分配4个单位空间就好了(0,1,2,3),并且给各个数值赋值为-1
arr[0]=-1;
arr[1]=-1;
arr[2]=-1;
arr[3]=-1;
(2)遍历到路径①:首先对根节点是否相同进行判断,节点0的根节点arr[0]:-1,因为根节点是初始赋值,所以根节点视为本身,所以arr[0]:0,同理节点1的根节点是arr[1]:1 两节点的根节点不同,合并一下 arr[0]=arr[1],所以节点0的根节点就发生了变化。
arr[0]=1;
arr[1]=1;
arr[2]=-1;
arr[3]=-1;
(2)遍历到路径②:首先对根节点是否相同进行判断,节点1的根节点arr[1]:1     节点2的根节点arr[2]:2(同上)    根节点不同    转存根节点arr[1]=arr[2]    到此节点1的根节点指向了节点2的根节点,并且所以以节点1为根节点的节点(在这里是节点0)都要转存根节点
arr[0]=2;
arr[1]=2;
arr[2]=2;
arr[3]=-1;
(3)遍历到路径③:首先对根节点是否相同进行判断,节点0的根节点arr[0]:2     节点3的根节点arr[3]:3(同上)    根节点不同    转存根节点arr[0]=arr[3]    到此节点0的根节点指向了节点3的根节点,并且所有和节点0根节点相同的节点(在这里是节点1,和节点2)都要转存根节点
arr[0]=3;
arr[1]=3;
arr[2]=3;
arr[3]=3;

到这里可能有人会问了,明明有四个节点是相同的,为什么不算是闭环,原因是,我们判断节点是否相同的时间点是在我们遍历路径的第一时间,如果两节点相同,那么就是能够组成闭环的,如果不相同,那么就是没有闭环,接下来我们依照图2继续遍历路径。

(4)遍历到路径④:首先对根节点是否相同进行判断,节点2的根节点arr[2]:3     节点3的根节点arr[3]:3    OK!OK!OK!停住停住,节点相同,有闭环。
arr[0]=3;
arr[1]=3;
arr[2]=3;
arr[3]=3;
至此,判断并查集的功能结束,我这种方法实际上需要耗费的空间还是蛮大的,但是相对于另一种网上的并查集查询的方法还是更容易理解的(鄙人个人认为),接下来出示另一种思路的分析过程:
(1)首先依旧是创建拥有节点个数的整形数组arr,用来存放各个节点的根节点,在此我们分配4个单位空间(0,1,2,3),并且给各个数值赋值为-1
arr[0]=-1;
arr[1]=-1;
arr[2]=-1;
arr[3]=-1;
(2)遍历到路径①:首先对根节点是否相同进行判断,节点0的根节点arr[0]:-1,因为根节点是初始赋值,所以根节点视为本身,所以arr[0]:0,同理节点1的根节点是arr[1]:1 两节点的根节点不同,合并一下 arr[0]=arr[1],所以节点0的根节点就发生了变化。
arr[0]=1;
arr[1]=1;
arr[2]=-1;
arr[3]=-1;
(2)遍历到路径②:首先对根节点是否相同进行判断,节点1的根节点arr[1]:1     节点2的根节点arr[2]:2(同上)    根节点不同    在转存根节点之前有其他的工作,就是寻找到真实根节点,因为节点0的根节点是1,所以节点0的根节点实际上是节点1的根节点:1 转存真实根节点arr[1]=arr[2]    到此,节点的变化如下:
arr[0]=1;
arr[1]=2;
arr[2]=2;
arr[3]=-1;
(3)遍历到路径③:首先对根节点是否相同进行判断,节点0的根节点arr[0]:1     节点3的根节点arr[3]:3(同上)    根节点不同    寻找到真实根节点,因为节点0的根节点是1,节点1的根节点是2所以节点0的真实根节点2:1转存根节点arr[2]=arr[3]    到此,节点的变化如下:
arr[0]=1;
arr[1]=2;
arr[2]=3;
arr[3]=3;

同样到这里了,我们依旧没有判断闭环

(4)遍历到路径④:首先对根节点是否相同进行判断,节点2的根节点arr[2]:3     节点3的根节点arr[3]:3    !!!停住停住,节点相同,有闭环。
arr[0]=1;
arr[1]=2;
arr[2]=3;
arr[3]=3;

代码实现

public class 并查集算法 {
	public static void main(String[] args) 
	{
		int[] arr= new int[4];
		get(arr);//给arr赋值
		int flage=0;
		int[][] edgs= //表示两个点相连的路径集合
			{
			{0,1},{1,2},{2,3},{0,3}
			};
		for(int i=0;i<4;i++)//进行循环来判断是否含有闭环
		{
			System.out.println("*************这是在进行第"+i+"次循环*************");
			int x=edgs[i][0];//x是这个集合每一个元素的第一个点
			int y=edgs[i][1];//y是这个集合每一个元素的第二个点
			//通过x和y的联立来判断是否含有闭环
			if(union(x,y,arr)==0)//因为函数中约定如果返回0的话那么久表示有闭环
			{
				flage=1;
			}
		}
		if(flage==0)
		{
			System.out.println("Haven't");
		}else 
		{
			System.out.println("Have");
		}
	}
	public static int find_root(int x,int arr[])//寻找根节结点函数
	{
		int x_root=x;//定义x的根节点就是x,这是假设步骤,下面进行寻找
		while(arr[x_root]!=-1)//如果这个假设的根节点的值不是一开始赋值的-1
		{
			System.out.println("当前值:"+x_root+"\tarr["+x+"]:"+arr[x_root]);
			x_root=arr[x_root];//就开始寻找这个假设的根节点的真实的根节点
		}
		return x_root;//返回这个根节点的值
	}
	public static int union(int x,int y,int arr[])//合并结点函数
	{
		//如果返回1表示可以成功的将其合并,如果返回0的时候表示合并失败
		int x_root = find_root(x,arr);//寻找x的根节点赋值给x_root
		System.out.println("x的根节点是:"+x_root);
		int y_root = find_root(y,arr);//寻找y的根节点赋值给y_root
		System.out.println("y的根节点是:"+y_root);
		if(x_root == y_root)//如果两个结点相同,证明可以将其合并,返回0
		{
			System.out.println("x和y的结点相同");
			return 0;
		}else //否则将y的根节点指向
		{
			System.out.println("x和y的根节点不同,所以把x的根节点指向了y,即将y_root:"
								+y_root+"赋值给了arr["+x_root+"]");
			arr[x_root]=y_root;
			System.out.println("此时arr["+x_root+"]:"+arr[x_root]);
			return 1;
		}
	}
	public static void get(int arr[])//给arr赋值的一个函数
	{
		for(int i=0;i<arr.length;i++)
		{
			arr[i]=-1;
		}
	}
}

思路2-穿针引线法:

具体思路和我认为的数组寻根法(第一开始讲到的那种)类似,废话不多说,直接走:
(1)这次创建的事的字符串数组arr,用来存放各个节点的所能够到达的节点,在此我们分配4个单位空间(0,1,2,3),并且给各个数值赋值为索引数值(因为本身是可以到达本身节点的)。
arr[0]=0;
arr[1]=1;
arr[2]=2;
arr[3]=3;
(2)遍历到路径①:首先对节点能够到达的地方进行判断,看看节点0和节点1之间是否互通(检查方式就是arr[0]中是否含有字符“1”或者arr[1]中是否含有“0”),节点0能够到达的路径是arr[0]:0,节点1能够到达的路径是arr[1]:1,因为路径①是节点0和节点1之间的路径,所以可以理解为节点0和节点1可以互相到达,所以更新节点如下:
arr[0]=01;
arr[1]=01;
arr[2]=2;
arr[3]=3;
(2)遍历到路径②:依然是看看节点能否互相到达,路径②是节点1和节点2之间的连接,节点1可以到达的节点是0和1,不可以到达节点2,然后进行更新:
arr[0]=012;
arr[1]=012;
arr[2]=012;
arr[3]=3;
(3)遍历到路径③:依然是看看节点能否互相到达,路径③是节点0和节点3之间的连接,节点0可以到达的节点是0和1和2,不可以到达节点3,进行更新:
arr[0]=0123;
arr[1]=0123;
arr[2]=0123;
arr[3]=0123;

同样到这里了,我们依旧没有判断闭环

(4)遍历到路径④:这个路径是节点2和节点3之间的连接,因为节点2可以到达的节点是0、1、2、3,能够到达节点3,因为已经能够到达了,又一次进行了连接,所以产生了闭环,直接跳出就可以了。
arr[0]=0123;
arr[1]=0123;
arr[2]=0123;
arr[3]=0123;

代码实现

public class 并查集 {
	static String[] point;
	static boolean flage = false;
	public static void main(String[] args)
	{
		@SuppressWarnings("resource")
		Scanner in =new Scanner(System.in);
		System.out.println("Please enter the number of points:");
		String s1 = in.nextLine();
		int x = Integer.valueOf(s1);
		point =  new String[x];
		for(int i=0;i<x;i++)
		{
			point[i]=i+"";
		}
		System.out.println("Please enter the number of edges:");
		String s2 = in.nextLine();
		int y = Integer.valueOf(s2);
		int[][] edgs= new int [y][2];
		for(int i=0;i<y;i++)
		{
			System.out.println("Please enter the node connected by the "+(i+1)+" edge (separated by space).:");
			String[] str = in.nextLine().split(" ");
			edgs[i][0]=Integer.valueOf(str[0]);
			edgs[i][1]=Integer.valueOf(str[1]);
		}
		for(int i=0;i<y;i++)
		{
			if(union(edgs[i][0],edgs[i][1])==0)
			{
				flage=true;
				break;
			}
		}
		if(flage)
		{
			int index = 0;
			System.out.println("Have");
			for(String s:point)
			{
				System.out.println("The node that node "+index+" can reach"+":\t"+s);
				index++;
			}
		}else 
		{
			System.out.println("Haven't");
		}
	}
	public static int union(int x,int y)
	{
		String s = String.valueOf(y);
		if(point[x].contains(s))
		{
			return 0;
		}else
		{
			String temp =point[x];
			point[x]=point[x]+s;
			point[y]=point[x];
			fill(temp,point[x]);
			return 1;
		}
	}
	public static void fill(String s,String str) 
	{
		for(int i=0;i<point.length;i++)
		{
			if(point[i].contains(s))
			{
				point[i]=str;
			}
		}
	}
}
控制台实例

Please enter the number of points:(请输入节点个数)
44个)
Please enter the number of edges:(请输入路径个数)
33条)
Please enter the node connected by the 1 edge (separated by space).:(第一条路径)
0 1(节点0和节点1之间有路径)
Please enter the node connected by the 2 edge (separated by space).:(第二路径)
1 2(节点1和节点2之间有路径)
Please enter the node connected by the 3 edge (separated by space).:(第三条路径)
0 3(节点0和节点3之间有路径)
Haven't(不含有闭环)



Please enter the number of points:(请输入节点个数)
44个)
Please enter the number of edges:(请输入路径个数)
44条)
Please enter the node connected by the 1 edge (separated by space).:(第一条路径)
0 1(节点0和节点1之间有路径)
Please enter the node connected by the 2 edge (separated by space).:(第二路径)
1 2(节点1和节点2之间有路径)
Please enter the node connected by the 3 edge (separated by space).:(第三条路径)
0 3(节点0和节点3之间有路径)
Please enter the node connected by the 4 edge (separated by space).:(第四条路径)
2 3(节点2和节点3之间有路径)
Have(存在闭环)
The node that node 0 can reach:	0123(节点0可以到达的节点:0123The node that node 1 can reach:	0123(节点1可以到达的节点:0123The node that node 2 can reach:	0123(节点2可以到达的节点:0123The node that node 3 can reach:	0123(节点3可以到达的节点:0123

总结

并查集并不是很难理解,可以说它是一个冷门必考点,基本上每一届都会有,但是没有那么多,不像深搜广搜那么频繁,能用的话也是要和其它算法相结合起来一起用,自己很难说能够单独解出来一道题。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丨耳东丨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值