并查集

概述

并查集是用来解决集合的合并和查找的数据结构。提供两个基本操作即

  • find(int i):查找I所归属的集合,通常使用find(i)和find(j)判断i和j是否连通,即是否属于同一个集合。
  • union(int i , int j):合并两个集合,操作后I所在集合和所有元素和J所在集合的所有元素都连通。

每个方法的对应思路:

  • 初始化:用森林表示图的连通性,每个节点都有一个指针指向父节点。一开始各个节点都是单独的,所以指向自己。
  • 合并:让节点较少的树连接到较多的树下面,避免出现链式情况。连通分量–。
  • 判断连通:若两个节点连通,则一定有相同的根节点。
  • 查找根节点:找到给定节点的根节点,用路径压缩在查找时顺手压缩树的高度为常数。

关键点:

  • parent[] 存每个节点的父节点;
  • size[] 记录每棵树的重量,让小重量的树连接在大重量树的下面;
  • find() 进行路径压缩,保证树高度为常数。

模板

public class UnionFind {
    //连通分量个数
    private int count;
    //存储一棵树
    private int[] parent;
    //记录树的高度/重量
    private int[] size;

    public UnionFind(int n) {
        this.count = n;
        parent = new int[n];
        size = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i;
            size[i] = 1;
        }
    }

    /**
     * 合并两个集合
     * @param p
     * @param q
     */
    public void union(int p, int q) {
        int rootP = find(p);
        int rootQ = find(q);
        if (rootP == rootQ) {
            return;
        }

        //调整平衡,小树连在大树之后
        if (size[rootP] > size[rootQ]) {
            parent[rootQ] = rootP;
            size[rootP] += size[rootQ];
        } else {
            parent[rootP] = rootQ;
            size[rootQ] += size[rootP];
        }
        count--;
    }

    public boolean connected(int p, int q) {
        int rootP = find(p);
        int rootQ = find(q);
        return rootP == rootQ;
    }

    /**
     * 找到元素归属的集合
     * @param x
     * @return
     */
    private int find(int x) {
        //路径压缩
        //每次把自己变成父结点的兄弟节点
        while (parent[x] != x) {
            parent[x] = parent[parent[x]];
            x = parent[x];
        }
        return x;
    }

    public int count() {
        return count;
    }
}

例题

深度搜索DFS的问题大多都能用并查集解决。

你可能感兴趣的:(常见算法思想,数据结构,算法)