并查集(Union Find Set):是一种用于处理分离集合的抽象数据类型。主要包含2种操作:
查询(Find):给定一个x,判断x属于哪个集合。
合并(Union):给定x和y,讲x所在的集合与y所在的集合合并。
实现并查集:
实现方法1:顺序表
在顺序表实现中,使用数组记录每个元素所属的集合代表。A[i] = j,表示元素i所属集合的代表为j
① 初始化:make-set(x):设置A[x] = x,N个元素的时间复杂度O(N)。
② 查找:find(x):返回A[x]的值,时间复杂度O(1)。
③ 合并:union(y, x):如果x和y属于同一个集合,不做任何操作,返回他们所属集合的
代表,否则更新其中一个集合中所有元素的代表为另一个集合的代表,时间复杂度O(N)。
实现方法2:双向链表
在链表实现中,每个分离集合对应一个链表,链表有一个表头(集合代表),每个元素有一个head指针指向表头,标记其所属集合,一个next指针指向集合中的下一个元素。为了union方便,再添加一个last指针,指向集合中的最后一个元素(只在集合代表中记录last)。
① 初始化:make-set(x):设置S[x].head = S[x].last = x , S[x].next = 0
② 查找:find(x):返回S[x].head。
③合并:union(x, y):如果x和y属于同一个集合,不做任何操作,返回S[x].head,否则将其中一个集合合并到另一个集合,返回合并后集合的代表。
实现方法3:树
在树实现中,每棵树表示一个分离集合,树中的每个节点表示集合的一个成员,每个成员都有一个指向其双亲的指针,用树根作为集合的代表,且根节点的父节点指向其自身。每个分离结合对应的一棵树称为“分离集合树”。
由于每个节点都存储其双亲节点,使用双亲表示法记录“分离集合树”。
双亲表示法:定义数组p[N],p[i]记录节点i的双亲。
① 初始化:make-set(x):p[x] = x。
② 查找:find(x):向上查找x的双亲,直到根节点,返回根节点的编号。
③ 合并:union(y,x):如果x和y属于同一个集合,不做任何操作,返回x所在分离集合树的根,否则将其中一棵分离集合树的根节点的双亲指向另一棵分离集合树的根节点。
考虑时间复杂度:
h表示x所在的“分离集合树”的深度。
初始化:复杂度O(N)
查找:要遍历x所在的“分离集合树”,直到找到根,时间复杂度为O(h)
查找x,y所在集合代表复杂度O(h),单修改复杂度:O(1),总复杂度O(h)
树的性能优化:
优化思想:降低“分离集合树”的深度。
优化·:按秩(rank)合并:在执行union操作时,优先将深度低的树合并到深度高的树中。
优化2:路径压缩:在执行find(x)操作时,将x及其祖先的双亲都更新为根节点,降低树的深度。
例如:下图所示的分离集合树在调用find(5)后会转变为右侧的B树,深度从5降为了3。
连通分量
概念1:连通图
连通图:如果无向图G中,任意两个顶点vi和vj都是连通的,则称G是连通图。
拥有n个顶点的连通图,至少有n - 1条边。
概念2:连通网
连通网:带权值的连通图称为连通网。
连通图:
非连通图:
连通网:
概念3:连通分量
连通分量:无向图中的极大连通子图称为连通分量,连通分量包含以下特征:
① 要是子图。
② 子图要是连通的。
③ 该连通子图含有极大顶点数。
④ 具有极大顶点数的连通子图包含依附于这些顶点的所有边。
并查集求连通分量:
并查集求连通分量时,无需预先存边,节省空间,时间复杂度也很低,对于每条边 (u, v) 进行一次union(u, v)操作,合并完成后,每个“分离集合”即是一个连通分量。
例题:
Farmer John and Betsy are playing a game with N (1 <= N <= 30,000)identical cubes labeled 1 through N. They start with N stacks, each containing a single cube. Farmer John asks Betsy to perform P (1<= P <= 100,000) operatio