【图解算法】并查集 —— 联合查找算法

WIKIWIKI 告诉我 —— 何为并查集

在计算机科学中,并查集(Union-Find)是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。 并查集存在两个操作(1.union 联合 2.find 查找) 和一个需要解答的问题(1.isConnected 或 isSameSet 是否是相互连接,或者说是否在同一个集中)

思考几个问题

  • wiki 告诉我们并查集这种数据结构可以解决一个问题(可以考察两个节点是否相通),那么并查集可以解决那些实际问题呢?
  • 我们实现并查集这种数据结构需要几个步骤呢(union、find、isConnected…)?

实现

我们这里直接采用基于 Rank 合并(合并时将元素所在深度小的集合合并到元素所在深度大的集合)方式的实现。

我们还是先看图

【图解算法】并查集 —— 联合查找算法_第1张图片

我们在这儿可以回答一下上面提出的第二个问题,实现并查集所需要的几个步骤:
1. 初始化元素
2. 实现元素与元素间的联合操作
3. 实现查找元素所在树的根节点
4. 解决一个问题,判定两个元素是否在同一棵树上(两个元素是否相互连接)

再来看代码

public class UnionFind {

    private int[] parent;   // 标注当前元素的父节点的位置

    private int[] rank;     // 标注当前元素的层级数

    private int size;       // 并查集中的元素个数

    public UnionFind (int size){
        this.size = size;

        parent = new int[size];
        rank = new int[size];

        init();
    }

    private void init() {
        for (int i = 0; i < size; i++) {
            // 初始化时所有的节点的父节点指向本身,所有的元素层级均为 1
            parent[i] = i;
            rank[i] = 1;
        }
    }


    /**
     * 寻找当前节点的根节点元素
     * @param target
     * @return
     */
    public int find(int target) {
        if(target >= size)
            throw new ArrayIndexOutOfBoundsException();
        if(target == parent[target])
            return target;
        return find(parent[target]);
    }

    /**
     * 连接两个元素
     * @param p
     * @param q
     */
    public void union(int p, int q) {
        int pRoot = find(p);
        int qRoot = find(q);
        if(pRoot == qRoot)
            return;
        if(rank[pRoot] > rank[qRoot]) { // p 所在的树的高度比 q 所在输的高度高,这时应该让 q 的根节点元素连接到 p 的根节点元素
            parent[qRoot] = pRoot; // 此时树的高度不变
        } else if(rank[pRoot] < rank[qRoot]) {
            parent[pRoot] = qRoot; // 此时树的高度不变
        } else {
            parent[pRoot] = qRoot; // 此时树的高度 +1
            rank[qRoot] += 1;
        }
    }

    /**
     * 判断两个节点是否连接
     * @param p
     * @param q
     * @return
     */
    public boolean isConnected(int p, int q) {
        // 如果两个节点的根节点一致则表示这两个节点是相连接的
        return find(p) == find(q);
    }

}

测试类:

public static void main(String[] args) {
    int size = 10;
    // Step 1: init()
    UnionFind uf = new UnionFind(size);

    // Step 2: union()
    uf.union(1,2);
    uf.union(3,4);
    uf.union(0,9);
    uf.union(4,7);
    uf.union(6,5);
    uf.union(5,8);
    uf.union(3,9);
    uf.union(1,8);

    // Step 3: find()
    System.out.println(uf.find(0));     // 9
    System.out.println(uf.find(1));     // 5
    System.out.println(uf.find(2));     // 5
    System.out.println(uf.find(3));     // 9
    System.out.println(uf.find(4));     // 9
    System.out.println(uf.find(5));     // 5
    System.out.println(uf.find(6));     // 5
    System.out.println(uf.find(7));     // 9
    System.out.println(uf.find(8));     // 5
    System.out.println(uf.find(9));     // 9

    // Step 4: isConnected
    System.out.println(uf.isConnected(0,1));    // false
    System.out.println(uf.isConnected(1,2));    // true
    System.out.println(uf.isConnected(3,4));    // true
    System.out.println(uf.isConnected(5,6));    // true
    System.out.println(uf.isConnected(7,8));    // false
    System.out.println(uf.isConnected(8,9));    // false
    System.out.println(uf.isConnected(2,4));    // false
    System.out.println(uf.isConnected(3,5));    // false
    System.out.println(uf.isConnected(5,6));    // true
    System.out.println(uf.isConnected(7,9));    // true
}

我们可以分解这几歩操作

Step 1: init()

【图解算法】并查集 —— 联合查找算法_第2张图片

Step 2: union()

【图解算法】并查集 —— 联合查找算法_第3张图片

Step 3: find()

if(target == parent[target]) 
    return target;
return find(parent[target]);

递归去寻找目标的父亲节点,直到寻找到根节点为止

Step 4: isConnected()

return find(p) == find(q);

考察两个节点的根节点是否是同一个,即考察两个节点是否在同一棵树上。

更多关于并查集

https://blog.csdn.net/dm_vincent/article/details/7655764

图解算法目录

【图解算法】排序算法——冒泡排序、选择排序

【图解算法】排序算法——插入排序

【图解算法】排序算法——归并排序

【图解算法】排序算法——快速排序

【图解算法】Java GC算法

【图解算法】排序算法——堆排序

【图解算法】并查集 —— 联合查找算法

【图解算法】排序算法——计数排序

Gif Power By https://visualgo.net

你可能感兴趣的:(算法,图解算法)