算法之并查集实现代码--java版本

并查集定义就不解释了两个用途:

  1. 找有多少个集合,常用于是否有关联

  2. 找无向图是否有环

找集合的代码:

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");
  }

}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值