并查集 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;
}

你可能感兴趣的:(Algorithm,-,java)