数据结构(11)-UnionFind(并查集)的实现与优化

本文深入探讨并查集这一数据结构,详细讲解其基本原理、实现方式及两种优化策略:高度优化与路径压缩,旨在解决动态连通问题,提高查询效率。

今天来看看并查集的实现与优化~- ( ゜- ゜)つロ

1.1.什么是并查集

并查集这种数据结构的出现是为了解决一类被称为动态连通的问题:
所谓的动态连通问题是指在一组可能相互连接也可能相互没有连接的对象中,判断给定的两个对象是否联通的一类问题。

在并查集中需要对外提供两个方法,一是find(int x, int y)方法,用于查询x和y之间是否连通;二是union(int x, int y)方法,用于将x和y建立连通关系;

1.2.并查集的实现

底层基于一个parent数组,使用parent[i] = j,用来标识i的父节点为j,这样就可以实现元素之间的连通关系;在初始化并查集时,将每个节点的父节点都初始化为自己;
在这里插入图片描述

parent01234567
element01234567

1. find(int x, int y)方法:
这个方法用于查询x和y是否连通,当x与y连通时返回true,反之返回false;实现的思路是判断两个元素的根节点是否为同一个即可判断:
在这里插入图片描述

/**
     * @auther: Arong
     * @description: 判断x和y是否属于同一集合
     * @param: [x, y]
     * @return: boolean
     * @date: 下午2:59 19-9-9
     */
    public boolean find(int x, int y) {
        return find(x) == find(y);
    }

    /**
     * @auther: Arong
     * @description: 查找child的根节点
     * @param: [child]
     * @return: int
     * @date: 下午3:06 19-9-9
     */
   private int find(int child) {
        if (child == parent[child]) return child;
        while (child != parent[child]) {
            child = parent[child];
        }
        return child;
    }

2. union(int x, int y)方法:
这个方法用于将x和y建立连通,即让他们拥有同一个根节点:
在这里插入图片描述

/**
    * @auther: Arong
    * @description: 将x和y连通
    * @param: [x, y]
    * @return: void
    * @date: 下午3:09 19-9-9
    */
   public void union(int x, int y) {
       //已是同一个祖先,已连通
       if (find(x) == find(y)) return;
       //让y的祖先成为x的父节点
       parent[find(x)] = find(y);
   }

1.3.优化1:高度优化

我们可以发现上述的实现中,find(int x)方法查找到根节点的时间和递归的深度有关,即和该节点到根节点的高度有关,当高度越低时,递归次数越少,搜索根节点的时间越短,但是按照我们的程序,可能出现这种情况:
即将2的根设置为4之后,整个连通集的高度增加了;但若设置4的根节点为2,效果都是一样的,但是连通集的高度仍为3;
所以此时可以维护一个rank数组,用来记录每个节点下树的高度;当进行union操作时,将高度小的那个节点设置为高度高的那个节点的子节点
在这里插入图片描述

/**
     * @auther: Arong
     * @description: 将x和y连通,增加rank判断
     * @param: [x, y]
     * @return: void
     * @date: 下午3:09 19-9-9
     */
    public void union(int x, int y) {
        //已是同一个祖先,已连通
        if (find(x) == find(y)) return;
        //高度大的节点为根节点
        if (rank[x] < rank[y]) {
            parent[x] = find(y);
            rank[y] += rank[x];
        } else if (rank[y] < rank[x]) {
            parent[y] = find(x);
            rank[x] += rank[y];
        } else {
            //两者高度相等
            parent[x] = find(y);
            rank[y] += 1;
        }
    }

1.4.优化2:路径压缩

所谓的路径压缩,指的是在查询是否连通的过程中去将连通集压缩成2层,以便加速下次查询的时间:
在这里插入图片描述

private int find(int child) {
        if (child == parent[child]) return child;
        if (child != parent[child]) {
            //路径压缩
            parent[child] = find(parent[child]);
        }
        return parent[child];
    }

1.5.总体代码实现

/**
 * @Auther: ARong
 * @Date: 19-9-9 下午2:56
 **/
public class UnionFind_Rank {
    //parent数组,标识每个节点对应的父节点,当父节点=当前节点时则表明其已为根节点
    private int[] parent;
    //rank数组维护着每个节点的高度
    private int[] rank;

    public UnionFind_Rank(int capacity) {
        parent = new int[capacity];
        rank = new int[capacity];
        for (int i = 0; i < capacity; i++) {
            //每个节点的父节点都初始化为当前节点
            parent[i] = i;
            rank[i] = 1;
        }
    }
    /**
     * @auther: Arong
     * @description: 判断x和y是否属于同一集合
     * @param: [x, y]
     * @return: boolean
     * @date: 下午2:59 19-9-9
     */
    public boolean find(int x, int y) {
        return find(x) == find(y);
    }

    /**
     * @auther: Arong
     * @description: 查找child的根节点
     * @param: [child]
     * @return: int
     * @date: 下午3:06 19-9-9
     */
    private int find(int child) {
        if (child == parent[child]) return child;
        if (child != parent[child]) {
            //路径压缩
            parent[child] = find(parent[child]);
        }
        return parent[child];
    }

    /**
     * @auther: Arong
     * @description: 将x和y连通
     * @param: [x, y]
     * @return: void
     * @date: 下午3:09 19-9-9
     */
    public void union(int x, int y) {
        //已是同一个祖先,已连通
        if (find(x) == find(y)) return;
        //高度大的节点为根节点
        if (rank[x] < rank[y]) {
            parent[x] = find(y);
            rank[y] += rank[x];
        } else if (rank[y] < rank[x]) {
            parent[y] = find(x);
            rank[x] += rank[y];
        } else {
            //两者高度相等
            parent[x] = find(y);
            rank[y] += 1;
        }
    }

}
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BoringRong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值