并查集 Union-Find

并查集 Union-Find

基本结构

public class UF {
    private int count;
    private int[] id;

    public UF(int n) {
        count = n;
        id = new int[n];
        for (int i = 0; i < n; i++) {
            id[i] = i;
        }
    }

    public int count() {
        return count;
    }

    public boolean connected(int p, int q) {
        return find(p) == find(q);
    }
    
    public int find(int p);
    public void union(int p, int q);
}

M - 边数 N - 节点数

Quick-Find ~ O ( M N ) O(MN) O(MN)

暴力法,将前一元素所在的连通分量中所有元素的标识全部重置为后一元素所在的连通分量

public int find(int p) {
    return id[p];
}

// 将所有id[p]全部重赋值为id[q]
public void union(int p, int q) {
    int pID = find(p);
    int qID = find(q);
    if (pID==qID) {
        return;
    }
    for (int i = 0; i < id.length; i++) {
        if (id[i] == pID) {
            id[i] = qID;
        }
    }
    // 每一次合并都会使连通分量总数减1
    count--;
}

Quick-Union ~ O ( M ) − O ( M N ) O(M)-O(MN) O(M)O(MN)

将连通分量当作树处理,以根节点作为各个分量的标识符,每次union只将两颗树的根节点合并。

public int find(int p) {
    // 向上追溯寻找根节点
	while (p != id[p]) {
        p = id[p];
    }
    return p;
}

public void union(int p, int q) {
	int pRoot = find(p);
	int qRoot = find(q);
	if (pRoot == qRoot) {
		return;
	}
    // 将一个分量的根节点连接到另一个分量的根节点
	id[pRoot] = qRoot;
    // 每一次合并都会使连通分量总数减1
	count--;
}

Weighted Quick-Union ~ O ( M l o g N ) O(MlogN) O(MlogN)

public class UF {
    private int count;
    private int[] id;
    private int[] height;	// 记录树高

    public UF(int n) {
        count = n;
        id = new int[n];
        height = new int[n];
        for (int i = 0; i < n; i++) {
            id[i] = i;
            height[i] = 0;
        }
    }

    public int count() {
        return count;
    }

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

    public int find(int p) {
        while (p != id[p]) {
            p = id[p];
        }
        return p;
    }

    public void union(int p, int q) {
        int pRoot = find(p);
        int qRoot = find(q);
        
        if (pRoot == qRoot) {
            return;
        }
        
        // 将矮树并入高树
        if (height[pRoot] < height[qRoot]) {
            id[pRoot] = qRoot;
        } else {
            id[qRoot] = pRoot;
            // 当且仅当两树高相同时才会使合并后的树高加1
            if (height[pRoot] == height[qRoot]) {
                height[pRoot]++;
            }
        }
        count--;
    }
}

Path Compression - 非递归

// Two-pass
public int find(int p) {
    int root = p;
    while (root != id[root]) {
        root = id[root];
    }
    
    while (p != root) {
    	int temp = id[p];
        id[p] = root;
        p = temp;
    }
    
    return p;
}

// One-pass
public int find(int p) {
    while (p != id[p]) {
        // 相当于减半搜索路径
        id[p] = id[id[p]];
        p = id[p];
    }
    return p;
}

Path Compression - 递归

public int find(int p) {
    if (id[p] == p) {
        return p;
    }
    
    int root = find(id[p]);
    id[p] = root;
    return root;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值