Disjoint set and poj 1611

本文介绍了一种并查集数据结构的实现方法,并通过解决POJ 1611问题来展示其应用。该实现包括查找、路径压缩和加权合并等关键操作。
package hello.test;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class DisjointSet {

	private final static int DEFAULT_SIZE = 10;
	private int[] parent;  // set array
	private int size;  // the size of the set array
	
	// constructor
	public DisjointSet() {
		this(DEFAULT_SIZE);
	}
	
	// constructor
	public DisjointSet(int size) {
		this.size = size;  // set number
		parent = new int[size + 1];
		
		for(int i = 0; i <= size; i++) {
			parent[i] = -1;  // every element of the set is one element set
		}
	}
	
	// get the ith element's value
	public int getParentNumber(int i) {
		if(i >= 0 && i < this.size + 1) 
			return parent[i];
		return -1;
	}
	
	// find the root of x
	public int find(int x) {
		if(parent[x] < 0) {
			return x;
		} else {
			return find(parent[x]);
		}
	}
	
	// find the root of i and compress the path
	public int collapsingFind(int i) {
		int j = 0;
		for(j = i; parent[j] > 0; j = parent[j]) {} // search the i's root j
		
		// compress the path from i to j, set all the node's root to j
		while(i != j) {
			int temp = parent[i];
			parent[i] = j;
			i = temp;
		}
		
		return j;  // return root
	}
	
	// union two set's roots
	public void union(int root1, int root2) {
		parent[root2] = root1;
	}
	
	// use root's number as rank to union two sets
	public int weightedUnion(int root1, int root2) {
		if(root1 == root2) return root1;
		
		int tmp = parent[root1] + parent[root2];  // the sum of two roots' node as the new root nodes
		if(parent[root2] < parent[root1]) {  // root2 has more nodes
			parent[root1] = root2;  // make root2 as the root of root1
			parent[root2] = tmp;  // new root nodes
			return root2;
		} else {  // root1 has more nodes
			parent[root2] = root1;  // make root1 as the root of root2
			parent[root1] = tmp;
			return root1;
		}
	}
	
	
	
	/**
	 * @param args
	 * @throws FileNotFoundException 
	 * solve poj 1611 The Suspects
	 */
	public static void main(String[] args) throws FileNotFoundException {
		long startTime = System.currentTimeMillis();   
		// TODO Auto-generated method stub
		@SuppressWarnings("resource")
		Scanner fin = new Scanner(new File("/home/will/myworld/workspace/HelloProject/src/hello/test/poj1611.txt"));
		int r1, r2;  // r1, r2 are roots number
		
		while(fin.hasNext()) {
			int n = fin.nextInt();
			int m = fin.nextInt();
			
			if(n == 0 && m == 0) break;
			
			DisjointSet ds = new DisjointSet(n);
			
			for (int i = 0; i < m; i++) {
				int k = fin.nextInt();
				int x = fin.nextInt();
				r1 = ds.collapsingFind(x);
				
				for(int j = 0; j < k - 1; j++) {
					int y = fin.nextInt();
					r2 = ds.collapsingFind(y);
					r1 = ds.weightedUnion(r1, r2);  // r1 becomes the new root, the left node will be union to r1
				}
			}
			
			System.out.println(-ds.getParentNumber(ds.collapsingFind(0)));
		}
		
		long endTime = System.currentTimeMillis();
		System.out.println("The run time is : " + (endTime - startTime) + " ms");
	}

}



 

<think>我们被要求解释DisjointSet(并查集)的定义和用途。根据之前的对话历史和提供的引用,我们可以总结如下:定义:并查集(DisjointSet)是一种用于处理不相交集合的数据结构,它主要支持两种操作:1.查找(Find):确定某个元素属于哪一个子集(或者说是确定某个元素的“根”)。2.合并(Union):将两个子集合并成一个集合。在并查集中,每个集合用一个代表元素(通常是集合的根节点)来标识。当我们需要合并两个集合时,只需将一个集合的根节点指向另一个集合的根节点。用途:并查集的主要用途是高效地管理元素的连通性。例如:-在图中检查两个节点是否连通,以及合并两个连通分量。-在Kruskal算法中用于判断边的两个顶点是否属于同一个连通分量以避免形成环。-在一些动态连通性问题中(如网络连接、社交网络的朋友圈等)。实现:根据引用[1][3][4],我们通常用一个数组(称为father或parent)来存储每个元素的父节点。初始时,每个元素自成一个集合,即father[i]=i。操作细节:1.初始化:每个元素都是自己的父节点(自己就是根)。2.查找(Find):通过不断向上查找直到找到根节点(根节点的特点是father[x]==x)。为了优化,我们使用路径压缩:在查找的过程中,将沿途上的节点的父节点都直接指向根,以减少后续查找的深度。3.合并(Union):先分别找到两个元素的根,如果根相同则已经在同一个集合,无需合并;否则,将一个根的父节点设为另一个根。为了平衡树的高度,我们还可以使用按秩合并(rank):总是将深度较小的树合并到深度较大的树上,这样树的高度不会增加。如果两棵树高度相同,合并后高度加一。根据引用[1]的描述,合并操作是将一个集合的根的父亲指向另一个集合的根。在引用[3]和[4]中,我们看到了具体的代码实现。我们接下来给出一个并查集的完整实现,包括路径压缩和按秩合并(虽然引用[3]和[4]的代码没有按秩合并,但引用[1]中提到了合并操作,我们在代码中增加按秩合并以优化)。同时,我们将用中文解释其定义和用途。此外,根据指令,我们需要在末尾生成相关问题。注意:用户现在想了解定义和用途,所以我们重点放在基础概念上,避免深入算法细节(如Kruskal算法已经讨论过,这里只需简单提及)。结构:1.定义并查集2.核心操作3.代码实现(参考多个引用,编写一个优化版本)4.并查集的用途5.相关问题</think> ### 并查集(Disjoint Set)定义与核心用途 #### 一、基本定义 并查集是一种**处理不相交集合合并与查询问题**的高效数据结构[^1],包含两个核心操作: 1. **Find(查询)**:确定元素所属的集合(通常返回集合代表元素) 2. **Union(合并)**:将两个不相交集合合并为一个新集合 ```mermaid graph TB subgraph 初始状态 A[0]-->A B[1]-->B C[2]-->C end subgraph 合并Union(0,1) A-->B B-->B C-->C end subgraph 合并Union(1,2) A-->B B-->B C-->B end ``` #### 二、核心操作原理 1. **存储结构** 使用`father[]`数组(又称`parent[]`)存储元素的父节点引用[^1][^3]: ```python # 初始化:每个元素独立成集合 father = [0,1,2,...,n-1] # father[i] = i ``` 2. **Find操作** - 功能:查找元素所在集合的根节点(代表元素) - 优化:**路径压缩**(Path Compression) ```python def find(x): if father[x] != x: father[x] = find(father[x]) # 递归压缩路径 return father[x] ``` 3. **Union操作** - 功能:合并两个元素所在集合 - 优化:**按秩合并**(Union by Rank) ```python rank = [0]*n # 存储树的高度 def union(x, y): rx, ry = find(x), find(y) if rx == ry: return # 已属同集合 # 矮树合并到高树上(避免树过高) if rank[rx] < rank[ry]: father[rx] = ry elif rank[rx] > rank[ry]: father[ry] = rx else: father[ry] = rx rank[rx] += 1 # 高度相同时树高+1 ``` #### 三、关键性质与性能 | 特性 | 说明 | |------|------| | **连通性检测** | 判断元素连通性:$find(a) = find(b)$ [^2] | | **时间复杂度** | 单次操作均摊$O(\alpha(n))$(阿克曼反函数)[^4] | | **空间复杂度** | $O(n)$(存储father和rank数组) | | **动态性** | 支持集合的实时合并与查询 | #### 四、主要应用场景 1. **图论算法** - Kruskal最小生成树算法中的环检测[^2] - 连通分量实时统计 2. **动态连通性问题** - 社交网络好友关系合并(如:合并好友圈) - 图像处理中的像素区域合并 3. **编译器设计** - 变量等价关系推断(如:并查集实现类型等价) 4. **棋盘类游戏** - 围棋/象棋的连通区域判定 #### 五、典型代码实现 ```python class DisjointSet: def __init__(self, n): self.father = list(range(n)) self.rank = [0] * n def find(self, x): # 路径压缩查询 if self.father[x] != x: self.father[x] = self.find(self.father[x]) return self.father[x] def union(self, x, y): # 按秩合并 rx, ry = self.find(x), self.find(y) if rx == ry: return False if self.rank[rx] < self.rank[ry]: self.father[rx] = ry elif self.rank[rx] > self.rank[ry]: self.father[ry] = rx else: self.father[ry] = rx self.rank[rx] += 1 return True ``` #### 六、性能优化意义 | 优化方法 | 未优化复杂度 | 优化后复杂度 | 原理 | |---------|------------|------------|------| | 普通并查集 | $O(\log n)$ | $O(\alpha(n))$ | 避免树退化成链 | | 路径压缩 | - | 查询扁平化 | 降低后续查询深度 | | 按秩合并 | - | 控制树高度 | 合并时平衡树结构 | > 其中$\alpha(n)$是阿克曼反函数,在$n ≤ 10^{100}$时$\alpha(n) ≤ 4$[^4] --- ### 相关问题 1. **路径压缩与按秩合并能否同时使用?它们如何相互影响时间复杂度?** 2. **如何用并查集检测图中是否存在环?时间复杂度是多少?** 3. **在动态图连通性问题中,并查集相比DFS/BFS有哪些优势?** 4. **并查集能否高效支持集合的分裂操作?为什么?** 5. **如何处理带权并查集问题(如食物链问题)?** [^1]: 并查集通过father数组组织树形结构维护集合关系 [^2]: 集合操作本质是通过操作树根实现的 [^3]: 路径压缩是保证查询高效的核心技术 [^4]: 按秩合并与路径压缩共同实现次线性复杂度
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值