并查集定义就不解释了两个用途:
-
找有多少个集合,常用于是否有关联
-
找无向图是否有环
找集合的代码:
package leetcode;
/**
* @author 江河
* @date 2019-09-13 14:51
* 是否压缩路径??
*/
/**
* 班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。
*
* 给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。
*
* 示例 1:
*
* 输入:
* [[1,1,0],
* [1,1,0],
* [0,0,1]]
* 输出: 2
* 说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
* 第2个学生自己在一个朋友圈。所以返回2。
* 示例 2:
*
* 输入:
* [[1,1,0],
* [1,1,1],
* [0,1,1]]
* 输出: 1
* 说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。
*/
public class leetcode547findCollection {
public static void main(String[] args) {
int[][] list = new int[][]{{1,1,0}, {1, 1, 0}, {0,0, 1}};
int num = findCircleNum(list);
System.out.println(num);
}
public static int findCircleNum(int[][] M) {
UF uf = new UF(M.length);
for (int i = 0; i < M.length - 1; i++) {
for (int j = i + 1; j < M.length; j++) {
//为1肯定有关联,传进去合并一个集合
if (M[i][j] == 1) {
uf.union(i, j);
}
}
}
return uf.count;
}
static class UF {
public int count;
private int[] id;
private int[] sz;
//无参构造初始化
public UF(int N) {
id = new int[N];
//开始自己是自己的根节点
for (int i = 0; i < N; i++) {
id[i] = i;
}
//初始化集合就自己根一个,有关联就合并集合
sz = new int[N];
for (int j=0;j<N;j++){
sz[j]=1;
}
//开始初始化有几个集合,有关联就减并合并集合
this.count = N;
}
public int find(int k) {
//将节点的父节点指向该节点的爷爷节点,不断找到根节点,根节点是指向自己的id[k] =k
while (id[k] != k) {
id[k] = id[id[k]];
k = id[k];
}
return k;
}
public boolean isConnection(int m, int n) {
//判断p,q节点的根节点是否相等
// int p = find(m);
// int q = find(n);
// if (p == q) {
// return true;
// }
// return false;
//下面等效上面
return find(m)==find(n);
}
//
public void union(int m, int n) {
//下面判断是否m,n是一个根节点一棵树,等价下面if语句判断p,q
// if (isConnection(m, n)) {
// return;
// }
int p = find(m);
int q = find(n);
if (p==q) {
return;
}
//本来有关联但是不再一个集合里,就需要合并,把小的集合合并到大的用sz数组判断
if (sz[p] < sz[q]) {
id[p] = q;
sz[q] += sz[p];
} else {
id[q] = p;
sz[p] += sz[q];
}
count--;
}
}
}
找是否有环代码:
package leetcode;
import static java.lang.System.exit;
/**
* 可以利用下面代码寻找无向图的环,是否存在环
* @author 江河
* @date 2019-09-13 21:02
* 压缩路径有吗??
*/
public class findCircle {
/**
* x,y是传入的两个有关联的的值
* @param x
* @param y
* 传入的父节点开始初始化是1自己
* @param parents
* rank用来判断集合的合并少的节点添加到多的节点里
* @param rank
* @return
*/
public static int findCircleNum(int x,int y,int[] parents,int[] rank) {
//这是个方法传入的值通过uf对象调用
UF uf = new UF();
return uf.union(x,y,parents,rank);
}
public static class UF{
/**
* 找x的父节点根
* @param x
* @param parents
* @return
*/
public int find_root ( int x, int[] parents){
int x_root = x;
while (parents[x_root] != 1) {
x_root = parents[x_root];
}
return x_root;
}
/**
*判断x,y是否有相同的根,有则有环,没有就比较大小添加合并
* @param x
* @param y
* @param parents
* @param rank
* @return
*/
public int union ( int x, int y, int[] parents,int[] rank){
int x_root = find_root(x, parents);
int y_root = find_root(y, parents);
if (x_root == y_root) {
return 0;
} else {
//如个x_root对于的rank大就把y_root的最后节点设置为x的
if (rank[x_root] > rank[y_root]) {
parents[y_root] =x_root;
rank[y_root]+=rank[x_root];
}else {
//小于反过来,但是等于添加那都行,就放这里面了
parents[x_root] = y_root;
rank[x_root]+=rank[y_root];
}
return 1;
}
}
}
public static void main(String[] args) {
int[][] edges = new int[][]{
{0,1},{1,2},{1,3},
{2,4},{3,4},{2,5}};
//我知道为啥要越界了,因为当这有6个数只0-5数字的时候,但是你开辟5个空间
//但parent实际还是6个数,会越界parents长度以数字个数计算
int[] parents = new int[6];
int[] rank = new int[6];
for (int j = 0; j < 6; j++) {
parents[j] = 1;
rank[j] =1;
}
for (int i=0;i<edges.length;i++) {
int a= edges[i][0];
int b= edges[i][1];
//findCircle fc = new findCircle();
if (findCircleNum(a,b,parents,rank) ==0){
System.out.println("Yes Cycle");
exit(0);
}
}
System.out.println("No Cycle");
}
}