并查集

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

比如图一:

上图中就是没有闭环的一个图,而下图(图二)就是一个有闭环的图

思路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_rootSystem.out.println("x的根节点是:"+x_root);int y_root = find_root(y,arr);//寻找y的根节点赋值给y_rootSystem.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:(请输入节点个数)
4(4个)
Please enter the number of edges:(请输入路径个数)
3(3条)
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:(请输入节点个数)
4(4个)
Please enter the number of edges:(请输入路径个数)
4(4条)
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可以到达的节点:0123)
The node that node 1 can reach: 0123(节点1可以到达的节点:0123)
The node that node 2 can reach: 0123(节点2可以到达的节点:0123)
The node that node 3 can reach: 0123(节点3可以到达的节点:0123)

总结

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

Java实现_算法_并查集相关推荐

  1. Python_机器学习_算法_第4章_4.决策树算法

    Python_机器学习_算法_第4章_4.决策树算法 文章目录 Python_机器学习_算法_第4章_4.决策树算法 决策树算法 学习目标 4.1 决策树算法简介 学习目标 小结 4.2 决策树分类原 ...

  2. Python_机器学习_算法_第1章_K-近邻算法

    Python_机器学习_算法_第1章_K-近邻算法 文章目录 Python_机器学习_算法_第1章_K-近邻算法 K-近邻算法 学习目标 1.1 K-近邻算法简介 学习目标 1 什么是K-近邻算法 1 ...

  3. Kruskal算法与并查集

    Kruskal算法与并查集 一.Kruskal算法 1. 概念 Kruskal算法就是按照图中各个边上的权值大小进行递增排序,以此来构造最小生成树. 2.重点解析 在由Kruskal实现最小生成树的过 ...

  4. java 哈希算法_选择Java密码算法第1部分-哈希

    java 哈希算法 抽象 这是涵盖Java加密算法的三部分博客系列文章的第1部分. 该系列涵盖如何实现以下功能: 使用SHA–512散列 使用AES–256的单密钥对称加密 使用RSA–4096的公钥 ...

  5. 简单易懂的并查集算法以及并查集实战演练

    文章目录 前言 一.引例 二.结合引例写出并查集 1. 并查集维护一个数组 2. 并查集的 并 操作 3. 并查集的 查 操作 4. 基本并查集模板代码实现--第一版(有错误后面分析) 4.1 Jav ...

  6. 【算法】并查集的运用

    并查集的概念 朋友圈 团伙问题 连通图 总结 并查集的概念 并查集顾名思义就是合并和查找,问题在于合并什么,查找什么.这里有一种朴素的思想来解释这两个问题.就是把这个想成一棵树.合并什么?就是把不在这 ...

  7. 算法总结 — 并查集

    参考:算法学习笔记(1) : 并查集 - 知乎 并查集 (disjoint set union) 是 最优美的数据结构之一 合并 (merge): 把两个集合合并 查找 (find): 查找一个元素的 ...

  8. 算法:并查集(四种方式)

    简单并查集 public class UnionFind {private int[] id;private int count;public UnionFind(int N) {count = N; ...

  9. Kruskal算法和并查集

    Kruskal算法 步骤: 第一步:给所有边按照从小到大顺序排列(直接使用库函数qsort / sort). 第二步:从小到大依次考察每条边(u,v),在执行第二步时会出现以下两种情况: 情况1:u和 ...

最新文章

  1. Nginx与Lua利用fpm打成rpm包
  2. 数据库正常运行,突然变慢的解决思路
  3. 【JSTL】--c:out演示--drp213
  4. Python类的多态和多态性
  5. root cause of error messagw for a mixed usage of 01 and 03
  6. LeetCode14最长公共前缀
  7. 编译器、Make和CMake之间的关系
  8. httprunner接口测试脚本自动生成
  9. Atitit 学习的本质 团队管理与培训的本质 attilax总结 1. 学习的定义 1 2. 学习的本质是数据的处理,海量的数据,处理能力有限的大脑 2 2.1. 摘要(缩小数据体量。。这个过程有
  10. 【Web前端】自制精简版的思源黑体ttf(728kb) - 包含2630个常用汉字+字母+数字+常用符号
  11. 基于jieba和哈工大信息检索研究中心同义词词林扩展版的同义词替换安装与测试
  12. ImageLoader----内存缓存
  13. JAVA的一些学习方法
  14. idea怎么设置成中文
  15. 迅为i.MX6开发板mqtt 移植教程(一)
  16. 阿里云服务器共享计算型 n4 实例详解/优惠价格/如何选择
  17. web设计中的色彩与心理学
  18. ESXi生命周期政策
  19. 2022中国数字城市竞争力百强榜
  20. 市场调研-临床前医疗设备检测服务市场现状及未来发展趋势

热门文章

  1. 学习:GridView中asp:BoundField的Visible=false时,无法取到这个字段的值
  2. pdf2htmlEX实现pdf转html 通过java执行命令生成页面
  3. libflex cydia源
  4. c++中的constexpr与inline
  5. 计算机软件版本号是什么意思,WDDM是什么意思 如何查看自己电脑的WDDM版本号?...
  6. 【计算机英语】23年1月份计算机学习过程中的单词汇总
  7. (二)使用Navicat将mssql数据库数据迁移到PostgreSql
  8. android button imagebutton 区别,ImageView子控件,ImageButton和ZoomButton使用
  9. 电脑卫生知多少 清洁图文教程
  10. 进程通信 - 管道通信