并查集--Java实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011240016/article/details/83958250

将展示三种不同的并查集实现,通过这样的演化过程来体会并查集的思想。
三种实现里,都用到了一个id数组,但是具体的含义略有不同。

quick-find

这种实现里,id数组长度为N,如果pq是相连的,那么它们的id数组里的值相同,即id[p] == id[q]

所以对于find函数,找到该元素的上级。

// 初始化
public QuickFindUF(int N) {
	id = new int[N];
	for(int i = 0; i < N; i++) {
		id[i] = i;
	}
}
public int find(int p) {
	return id[p];
}
// 将p和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; // 将pID组件元素全都交给qID吧,这是任意的,可以反过来全部交给pID
	}
}

这种写法,查询pq是否连通是很快的,是O(1)时间复杂度,而连接起来则是O(N)时间复杂度。这还是单次操作,也就是说,总体来看,Union的操作需要O(N^2)复杂度。

现在我们把注意力转向优化Union操作。

quick-union

这里的id数组是有额外含义的,id[i]的值是i元素的父亲。那么,按照这个逻辑推演,i元素的根就是id[id[...id[i]...]],一直往前,直到该值不再变化为止。

所以,判断pq是否在一个组件内,只需要各自找到自己的root值,判断它们的root值是否相等即可。

public int find(int p) {
	while(p != id[p]) p = id[p]; // 一路向上
	return p;
}
public void join(int p, int q) {
	int pRoot = find(p);
	int qRoot = find(q);
	if (pRoot == qRoot) return;
	id[pRoot] = id[qRoot]; // 直接将其中一个root值变成另外一个的小弟
}

weighted quick-union

在上面join时,只是随意将其中一个对象的根变成另一个的小弟,没有进行过滤,这样导致的结果是,树可能会很不平衡,从而在查找时耗时很长。

改进也很简单,跟踪一下树的尺寸,将小一点的树连接到大一点的树,即小一点的作为小弟即可。

Weight-Union

public class WeightedQuickUnionUF {
	private int[] id;
	private int[] sz; // size of component for roots
	private int count; // number of components
	
	public WeightedQuickUnionUF(int N) {
		count = N; // 初始时有N个组件
		id = new int[N];
		sz = new int[N];	
		for(int i = 0; i < N; i++) {
			id[i] = i;
			sz[i] = 1;
		}
	}
	public int count() {
		return count;
	}
	public boolean connected(int p, int q) {
		return find(p) == find(q);
	}
	private int find(int p) {
		while(p != id[p]){
			p = id[p];
		}
		return p;
	}
	public void union(int p, int q) {
		int i = find(p);
		int j = find(q);
		if (i == j) return;
		if (sz[i] < sz[j]) {
			id[i] = j;
			sz[j] += sz[i];
		} else {
			id[j] = i;
			sz[i] += sz[j];
		}
		count--; // 执行一次union,才需要减一次
	}
}

到此为止,我们学习了三种方式,逐渐优化。但是还有进一步的可能,那就是路径压缩

扫描二维码关注公众号,回复: 4106875 查看本文章

路径压缩非常简单,只需要额外的一行即可:

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

END.

猜你喜欢

转载自blog.csdn.net/u011240016/article/details/83958250