快速并查集(Java实现)

       在一些应用的问题中,需将n个不同的元素划分成一组不相交的集合。开始时,每个元素自成一格单元素集合,然后按一定顺序将属于同一组的元素的集合合并。其间要反复用到查询某个元素属于哪个集合的运算。适合于描述这类问题的抽象数据类型称为并查集。


1. 并查集的概述

并查集的数学模型是一组不相交的动态集合的集合S={A,B,C,...},它支持以下的运算:

(1)union(A,B):将集合A和B合并,其结果取名为A或B;

(2)find(x):找出包含元素x的集合,并返回该集合的名字。


       在并查集中需要两个类型的参数:集合名字的类型和元素的类型。在许多情况下,可以用整数作为集合的名字。如果集合中共有n个元素,可以用范围【1:n】以内的整数表示元素。实现并查集的一个简单方法是使用数组表示元素及其所属子集的关系。其中,用数组下标表示元素,用数组单元记录该元素所属的子集名字。如果元素类型不是整型,则可以先构造一个映射,将每个元素映射成一个整数。这种映射可以用散列表或其他方式实现。


2. 并查集的实现

       采用数结构实现并查集的基本思想是,每个集合用一棵树表示。树的结点用于存储集合中的元素名。每个树结点还存放一个指向其父结点的指针。数根结点处的元素代表该数所表示的集合。利用映射可以找到集合中所对应的数结点。

       父亲数组是实现上述数结构的有效方法。

       Java实现代码:

public class UnionFind {
	Node[] node;   //父亲数组
	//并查集中的结点
	private static class Node{
		int parent;
		boolean root;
		
		private Node(){
			parent = 1;
			root = true;
		}
	}
	//将每个元素初始化为一颗单结点树
	public UnionFind(int n){
		node = new Node[n + 1];
		for(int e= 0; e <= n; e++){
			node[e] = new Node();
		}
	}
}
       find运算就是从元素e相应的结点走到树根处,找出所在集合的名字。
	public int find(int e){
		while(!node[e].root){
			e = node[e].parent;
		}
		return e;
	}
       union运算,合并两个集合,只要将表示其中一个集合的树的数根改为表示另一个集合的树的数根的儿子结点。

	public void union(int a, int b){
		node[a].parent += node[b].parent;
		node[b].root = false;
		node[b].parent = a;
	}


3. 快速并查集的实现

       容易看出,在最坏情况下,合并可能使n个结点的树退化成一条链,导致对所有元素各执行一次find将耗时O(n^2)。可做出以下改进来加速find运算。

1.)小树合并到大树

       在union操作中,将表示小树的数根改为表示大树的数根的儿子结点。于是并查集中每个结点至多被移动O(logn)次,从而每个结点到数根的距离不会超过O(logn)。所有,每次find运算只需O(logn)时间。

	/*
	 * 合并两个集合(加速)
	 * 将表示小树的数根改为表示大树的数根的儿子结点
	 */
	public void union(int a, int b){
		if(node[a].parent < node[b].parent){
			node[b].parent += node[a].parent;
			node[a].root = false;
			node[a].parent = a;
		}else{
			node[a].parent += node[b].parent;
			node[b].root = false;
			node[b].parent = a;
		}
	}

2.)路径压缩技术

       在执行find时,实际上找到了从一个结点到数根的一条路径。路径压缩就是把这条路上所有的结点都提升1层,以加快找到根结点的时间。
	/*
	 * find运算(加速)
	 * 从元素e相应的结点走到树根处,找出所在集合的名字
	 */
	public int find(int e){
		int current = e, p ,gp;
		/*排除当前结点或其父结点为根的情况后,加速find*/
		if(node[current].root){
			return current;
		}
		p = node[current].parent;
		if(node[current].root){
			return p;
		}
		gp = node[current].parent;
		
		while(true){
			node[current].parent = gp;
			if(node[gp].root){
				return gp;
			}
			current = p;
			p = gp;
			gp = node[p].parent;
		}
	}

       (并查集实现的详细代码及更多相关数据结构的代码均在 git )


参考资料:

1.《算法设计与分析》



  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 并查集是一种常用的数据结构,用于解决联通性问题,通常应用于图论中。以下是一个使用Java实现并查集的示例。 假设我们有一个由10个节点构成的图,节点编号分别为0-9。现在我们需要将这10个节点分成3个集合,其中第一个集合包括节点0,1,2,第二个集合包括节点3,4,5,第三个集合包括节点6,7,8,9。我们可以使用并查集实现这个目标。 下面是使用Java实现并查集的示例代码: ```java public class UnionFind { int[] parent; public UnionFind(int n) { parent = new int[n]; for (int i = 0; i < n; i++) { parent[i] = i; } } public int find(int x) { if (parent[x] == x) { return x; } return parent[x] = find(parent[x]); } public void union(int x, int y) { int px = find(x); int py = find(y); parent[px] = py; } public static void main(String[] args) { UnionFind uf = new UnionFind(10); uf.union(0, 1); uf.union(1, 2); uf.union(3, 4); uf.union(4, 5); uf.union(6, 7); uf.union(7, 8); uf.union(8, 9); System.out.println(uf.find(0) == uf.find(2)); System.out.println(uf.find(3) == uf.find(5)); System.out.println(uf.find(6) == uf.find(9)); } } ``` 这个示例中,我们首先创建一个长度为10的并查集,表示有10个节点。然后我们调用`union()`方法将节点分成3个集合。最后,我们可以使用`find()`方法查询两个节点是否在同一个集合中。 运行这个示例代码,可以看到输出结果为: ``` true true true ``` 这说明节点0、1、2在同一个集合中,节点3、4、5在同一个集合中,节点6、7、8、9在同一个集合中,符合我们的预期。 ### 回答2: 并查集是一种用于处理元素分组和查找的数据结构。在JAVA中,可以使用数组和指针实现一个并查集。 举个例子来说明,假设有5个元素{1, 2, 3, 4, 5},初始时每个元素都是独立的组,即每个元素是一个独立的集合。下面以操作序列为例,演示并查集的使用。 1. 初始化并查集,分别给每个元素设置一个集合代表元素。 假设初始时集合代表元素为{1, 2, 3, 4, 5}。 2. 根据需求进行合并操作,将元素合并到同一个组中。 假设合并操作为(1, 2),即将元素1和元素2合并到同一个组中。此时集合代表元素为{1, 2, 3, 4, 5},但元素1和元素2在同一个组。 3. 再进行合并操作,合并元素3和元素4到同一个组中。 合并操作为(3, 4),此时集合代表元素为{1, 2, 3, 3, 5},元素3和元素4在同一个组。 4. 进行查找操作,判断两个元素是否在同一个组。 查找操作为(2, 4),根据之前的合并操作,元素2和元素4应该在同一个组中。结果为true。 5. 再进行查找操作,判断元素1和元素5是否在同一个组。 查找操作为(1, 5),元素1和元素5没有进行过合并操作,所以不在同一个组中。结果为false。 通过以上例子可以看出,并查集可以快速判断两个元素是否在同一个组中,以及合并两个元素到同一个组中。并查集在图论、网络连通性等问题中有广泛应用。 ### 回答3: JAVA 并查集是一种用于处理不相交集合合并与查询问题的数据结构。它主要涉及两个基本操作:合并和查找。 举个例子来说明并查集的使用。假设有一个朋友圈的关系网,我们需要根据人们的好友关系构建一个并查集。 我们可以创建一个Person类来表示每个人,其中包含两个属性:id和parent。 ```java class Person { int id; int parent; public Person(int id) { this.id = id; this.parent = id; // 初始化时每个人的父节点都是自己 } } ``` 然后我们创建一个并查集类,并实现合并和查找操作。 ```java class UnionFind { Person[] people; public UnionFind(int n) { people = new Person[n]; for (int i = 0; i < n; i++) { people[i] = new Person(i); } } public int find(int x) { if (people[x].parent != x) { people[x].parent = find(people[x].parent); // 路径压缩 } return people[x].parent; } public void union(int x, int y) { int rootX = find(x); int rootY = find(y); if (rootX != rootY) { people[rootX].parent = rootY; // 合并两个集合 } } } ``` 接下来,我们可以使用并查集来处理好友关系,并查找某两个人是否属于同一个朋友圈。 ```java public class Main { public static void main(String[] args) { int n = 5; // 总共有5个人 UnionFind uf = new UnionFind(n); uf.union(0, 2); // 将人0和人2合并到同一个朋友圈 uf.union(1, 3); // 将人1和人3合并到同一个朋友圈 System.out.println(uf.find(0) == uf.find(2)); // 输出true,说明人0和人2属于同一个朋友圈 System.out.println(uf.find(1) == uf.find(4)); // 输出false,说明人1和人4不属于同一个朋友圈 } } ``` 通过以上示例,我们可以看出JAVA 并查集可以帮助我们快速并且高效地处理不相交集合合并与查询问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值