Java实现并查集

目录

一. 并查集原理

二. 并查集实现

三. 并查集应用(Leetcode)

一. 并查集原理

    在一些应用问题中,需要将n个不同的元素划分成一些不相交的集合。
    开始时,每个元素自成一个单元素集合,然后按一定的规律,将归于同一组元素的集合合并。在此过程中要反复用到查询某一个元素归属于哪个集合的运算。

    适合于描述这类问题的抽象数据类型称为 并查集(union-findset)

并查集通常使用一个数组来表示:

  1. 数组的下标对应集合中元素的编号
  2. 数组中如果为负数,负号代表根,数字代表该集合中元素个数
  3. 数组中如果为非负数,代表该元素双亲在数组中的下标

    初始化时,每个元素的根都是它自己,数组中的值均初始化为 -1,即每个元素自成一个集合。

    比如 0 3 5 是一个集合,1 2 6 8 是一个集合,4 7 9 是一个集合,此时的数组:

 

    如果将 0 1 两个集合合并:

 

并查集的应用场景:

  • 操作系统:在处理进程同步时,判断两个进程是否属于同一个同步集合
  • 图最小生成树算法:判断图中两个节点是否连通,确定最小生成树
  • 社交网络分析:确定社交网络中两个人是否可以通过某种关系链条相连;找出社交网络中的集团或者社区结构。

二. 并查集实现

import java.util.Arrays;

public class UnionFindSet {
    public int[] elem;//底层是一个数组
    public int n;

    UnionFindSet(int n){
        this.n = n;
        elem = new int[n];
        Arrays.fill(elem, -1);//整体初始化为-1:代表根
    }

    //查找根
    public int findParent(int index){
        if(index < 0 || index >= n){
            throw new ArrayIndexOutOfBoundsException("下标不合法");
        }
        while(elem[index] >= 0){
            index = elem[index];
        }
        return index;
    }
    //判断两个数字 是不是在同一个集合当中
    public boolean isSameSet(int index1, int index2){
        return findParent(index1) == findParent(index2);
    }

    //获取集合的个数
    int getSetCount(){
        int count = 0;
        for(int i : elem){
            if(i < 0){
                count++;
            }
        }
        return count;
    }
    void print(){
        System.out.println(Arrays.toString(elem));
    }


    //合并x1和x2,但是有个问题,x1和x2必须都是根;所以,先得查找x1和x2的根
    public void union(int index1, int index2){
        int parent1 = findParent(index1);
        int parent2 = findParent(index2);

        if(isSameSet(index1, index2)){
            //说明x1和x2的根是相同的 不进行合并
            return;
        }

        elem[parent1] = elem[parent2]+elem[parent1];
        elem[parent2] = parent1;

    }
}

三. 并查集应用(Leetcode)

等式方程的可满足性:

   给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4,并采用两种不同的形式之一:"a==b" 或 "a!=b"。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。只有当可以将整数分配给变量名,以便满足所有给定的方程时,才返回 true,否则返回 false。
 public boolean equationsPossible(String[] equations) {
        UnionFindSet unionFindSet = new UnionFindSet(26);
        //必须先添加完 == 之后,再遍历一遍 判断!= 的是否在一个集合中
        for(String s:equations){
            if(s.charAt(1) == '='){
                unionFindSet.union(s.charAt(0)-'a', s.charAt(3)-'a');
            }
        }
        // 如果是 != 但还在同一个集合,则返回false
        for(String s:equations){
            if(s.charAt(1) == '!'){
                if(unionFindSet.isSameSet(s.charAt(0)-'a', s.charAt(3)-'a')){
                    return false;
                }
            }
        }
        return true;
    }

 省份数量:

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。
public int findCircleNum(int[][] isConnected) {
        int n = isConnected.length;
        UnionFindSet unionFindSet = new UnionFindSet(n);
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                //如果相连,合并到同一个集合
                if(isConnected[i][j] == 1){
                    unionFindSet.union(i,j);
                }
            }
        }
        return unionFindSet.getSetCount();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值