【数据结构与算法 | 图篇】并查集(DisjointSet)

1. 前言

并查集(Disjoint Set)是一种树形的数据结构,用于处理一些不交集的合并及查询问题。它在图算法、计算机科学和编程竞赛中非常常见。并查集主要用于解决动态连通性问题,即在一个过程中维护一组不相交的集合,并支持两种基本操作:

  1.  查找(Find):确定一个元素属于哪个集合。通常实现为查找该元素所在集合的代表元或根节点。
  2. 合并(Union):将两个集合合并成一个集合。

2. 基本操作

1. 初始化

  • 对于每个元素 `x`,创建一个新的只包含自身的集合。这可以通过设置每个元素的父节点为自己来实现。

2. 查找 (Find)

  • 给定一个元素 `x`,通过沿着父节点链向上追溯,直到找到根节点。根节点的特征是它的父节点是指向自己的。
  • 为了提高效率,可以使用“路径压缩”技术:在查找过程中,将沿途的所有节点直接连接到根节点上。这样下一次查找时会更快。

3. 合并 (Union)

  • 给定两个元素 `x` 和 `y`,先找到它们各自所在的集合的根节点 `root_x` 和 `root_y`。
  • 如果 `root_x` 和 `root_y` 不相同,则将其中一个根节点的父节点设为另一个,从而将两个集合合并成一个。

3. 实现细节

并查集可以通过数组或者更复杂的数据结构来实现。最常见的实现方式是使用数组来存储每个元素的父节点信息。例如,对于一个包含 `n` 个元素的集合,可以定义一个长度为 `n` 的数组 `parent[]`,其中 `parent[i]` 存储了第 `i` 个元素的父节点的索引。
所以下面用数组来实现并查集。

4. 代码

初始时顶点集合:

public class DisjointSet {
    // 用数组来实现并查集
    // 牢大数组,存储着第i个元素在其集合中的牢大
    //当然,初始的时候每个元素的牢大都是它自己
    int[] parent;
    // 为union方法所用,记录集合的元素个数
    int[] size;

    public DisjointSet(int size) {
        parent = new int[size];
        this.size = new int[size];
        for(int i = 0; i < size; i++) {
            parent[i] = i;
            // 初始化时每个顶点都当做是一个集合,此时每个集合的长度当然都是1
            this.size[i] = 1;
        }
    }
    // 查找牢大的方法
    public int find(int x) {
        // 如果x与元素值相同,说明x就是牢大
        // 比如[0, 0, 1, 2],x=0时,x==parent[0],0是顶点集合0, 1的牢大
        if(x == parent[x]){
            return x;
        } else {
            // 路径压缩,将沿途的所有顶点都连接到集合中的牢大顶点上
            return parent[x] = find(parent[x]);
        }
    }
    // union是让两个集合"相交", 即选出新的牢大,x, y是原来牢大索引
    public void union(int x, int y) {
        // 让元素更多的集合中的牢大,继续成为牢大
        // 少的集合中的牢大成为新集合的小弟
        if(size[x] < size[y]){
            parent[x] = y;
            size[y] = size[x] + size[y];
        } else {
            parent[y] = x;
            size[x] = size[x] + size[y];
        }
    }

    public static void main(String[] args) {
        DisjointSet set = new DisjointSet(7);
        set.union(0, 1);
        set.union(1, 2);
        set.union(2, 3);
        set.union(3, 4);
        set.union(4, 5);
        set.union(5, 6);
        set.find(6);
        // set.find(6)对全路径做了路径压缩,使得其他所有节点的都直接指向了牢大节点0
        System.out.println(set);
        //[0, 0, 0, 0, 0, 0, 0]
    }

    @Override
    public String toString() {
        return Arrays.toString(parent);
    }
}

调用set.find(6)之前顶点图:

调用之后:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值