Union-find 并查集

本文详细介绍了并查集算法的四种实现方式:Quick-find、Quick-union、Weighted Quick-union 和 Weighted Quick-union with Path Compression。每种实现方式都分析了其优缺点,并给出了具体的复杂度对比。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

解决问题

给一系列对点0~N-1的连接,判断某两个点p与q是否相连。

private int[] id;

// 判断p和q是否属于同一个连通分量
public boolean connected(int p, int q) 

// 连接两个点
public void union(int p, int q)

  

Quick-find

connected(p, q):判断p 和 q 的id值是否相同

union(p, q): 将与p 的id 相同的所有点都改为q的id

缺点:union太慢,需要遍历id数组

Quick-union

connected(p, q):判断p 和 q 的根的id值是否相同

union(p, q): 将与p 的根的 id 改为q的根的 id

本质上是将并查集之间的关系看做一棵树

缺点:最坏情况下仍然需要遍历数组

Weighted Quick-union

connected(p, q):判断p 和 q 的根的id值是否相同

union(p, q): 判断p和q所在的树哪个大(包含的节点多),将较小的树根的 id 改为较大的树根的 id

某个节点高度增加1,当且仅当它在一颗小树T1上且被union并入大树T2中,生成的树节点数大于T1的两倍,所以某个节点的高度最多只能是lg(N)

Weighted Quick-union with Path Compression

connected(p, q):判断p 和 q 的根的id值是否相同

union(p, q): 判断p和q所在的树哪个大(包含的节点多),将较小的节点到较小的树根这条路径上所有节点的 id 改为较大的树根的 id

 

总结

四种方法复杂度如下,其中lg* 表示需要取对数多少次才能将N的值变为≤1,WQUPC复杂度是由论文中所得,lg*可以视为常数复杂度。

algorithm初始化unionconnected
quick findNN1
quick unionNNN
weighted quick unionNlg Nlg N
weighted quick union with path compressionNlg* Nlg* N

 实现

public class UF {

    private int[] parent;  // parent[i] = parent of i
    private byte[] rank;   // rank[i] = rank of subtree rooted at i (never more than 31) 记录的是树的高度
    private int count;     // number of components

    /**
     * Initializes an empty union-find data structure with <tt>N</tt> sites
     * <tt>0</tt> through <tt>N-1</tt>. Each site is initially in its own 
     * component.
     *
     * @param  N the number of sites
     * @throws IllegalArgumentException if <tt>N < 0</tt>
     */
    public UF(int N) {
        if (N < 0) throw new IllegalArgumentException();
        count = N;
        parent = new int[N];
        rank = new byte[N];
        for (int i = 0; i < N; i++) {
            parent[i] = i;
            rank[i] = 0;
        }
    }

    /**
     * Returns the component identifier for the component containing site <tt>p</tt>.
     *
     * @param  p the integer representing one site
     * @return the component identifier for the component containing site <tt>p</tt>
     * @throws IndexOutOfBoundsException unless <tt>0 ≤ p < N</tt>
     */
    public int find(int p) {
        validate(p);
        while (p != parent[p]) {
            parent[p] = parent[parent[p]];    // path compression by halving 【完成路径压缩】
            p = parent[p];
        }
        return p;
    }

    /**
     * Returns the number of components.
     *
     * @return the number of components (between <tt>1</tt> and <tt>N</tt>)
     */
    public int count() {
        return count;
    }
  
    /**
     * Returns true if the the two sites are in the same component.
     *
     * @param  p the integer representing one site
     * @param  q the integer representing the other site
     * @return <tt>true</tt> if the two sites <tt>p</tt> and <tt>q</tt> are in the same component;
     *         <tt>false</tt> otherwise
     * @throws IndexOutOfBoundsException unless
     *         both <tt>0 ≤ p < N</tt> and <tt>0 ≤ q < N</tt>
     */
    public boolean connected(int p, int q) {
        return find(p) == find(q);
    }
  
    /**
     * Merges the component containing site <tt>p</tt> with the 
     * the component containing site <tt>q</tt>.
     *
     * @param  p the integer representing one site
     * @param  q the integer representing the other site
     * @throws IndexOutOfBoundsException unless
     *         both <tt>0 ≤ p < N</tt> and <tt>0 ≤ q < N</tt>
     */
    public void union(int p, int q) {
        int rootP = find(p);
        int rootQ = find(q);
        if (rootP == rootQ) return;

        // make root of smaller rank point to root of larger rank
        if      (rank[rootP] < rank[rootQ]) parent[rootP] = rootQ;
        else if (rank[rootP] > rank[rootQ]) parent[rootQ] = rootP;
        else {
            parent[rootQ] = rootP;
            rank[rootP]++; //【只有此处才增加联通分量的rank】
        }
        count--;
    }

    // validate that p is a valid index
    private void validate(int p) {
        int N = parent.length;
        if (p < 0 || p >= N) {
            throw new IndexOutOfBoundsException("index " + p + " is not between 0 and " + (N-1));  
        }
    }

    /**
     * Reads in a an integer <tt>N</tt> and a sequence of pairs of integers
     * (between <tt>0</tt> and <tt>N-1</tt>) from standard input, where each integer
     * in the pair represents some site;
     * if the sites are in different components, merge the two components
     * and print the pair to standard output.
     */
    public static void main(String[] args) {
        int N = StdIn.readInt();
        UF uf = new UF(N);
        while (!StdIn.isEmpty()) {
            int p = StdIn.readInt();
            int q = StdIn.readInt();
            if (uf.connected(p, q)) continue;
            uf.union(p, q);
            StdOut.println(p + " " + q);
        }
        StdOut.println(uf.count() + " components");
    }
}

  

转载于:https://www.cnblogs.com/ericxing/p/4402725.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值