0%

并查集侦查无向图中的环

并查集是一种数据结构,它跟踪一个集合的元素被分割成若干不相干(不重叠)的子集。union-find算法是一种对这种数据结构执行两种有用操作的算法:

Find:确定一个特定的元素在哪个子集里。这可以用来确定两个元素是否在同一个子集中。

Union:将两个子集连接成一个单一的子集。

在这篇文章中,我们将讨论并查集的应用。这个应用就是检查一个给定的图是否有环。

UnionFInd算法可以检查一个无向图是否有环。注意到我们已经讨论过一种检查环的方法。这是另一种使用并查集的防范。这个方法假设图中没有自环。

我们可以在一个1D数组中跟踪子集,我们称它为parent[]。

我们考虑如下图:

对于每条边,用边的两个顶点做子集。如果两个顶点都已经在同一个子集中,就发现了一个环。

初始化时,parent的所有槽位都初始化为-1(意味着每个子集中只有一个项目)。

1
2
0   1   2
-1 -1 -1

现在逐一处理所有的边。

边0-1:找到顶点0和1所在的子集。由于它们在不同的子集中,我们对它们进行union操作。为了取union,可以让结点0作为节点1的父结点,或者反之。

1
2
0   1   2    <----- 1 is made parent of 0 (1 is now representative of subset {0, 1})
1 -1 -1

边1-2:1已经在subset 1中,2在subset 2中,所以取union

1
2
0   1   2    <----- 2 is made parent of 1 (2 is now representative of subset {0, 1, 2})
1 2 -1

边0-2:0在subset 2中,2也在subset 2中。因此,包括这条边形成了一个环。

为什么0的子集和和2相同?

0->1->2 // 1是0的父结点2是1的父结点。

根据以上解释,下面是实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# undirected_graph.py
from collections import defaultdict


class UnionFind:
def __init__(self, k):
self.uf = [-1] * (k + 1)
self.sets_count = k

def find(self, p):
if self.uf[p] < 0:
return p
self.uf[p] = self.find(self.uf[p])
return self.uf[p]

def union(self, p, q):
p_root = self.find(p)
q_root = self.find(q)
if p_root == q_root:
return
if self.uf[p_root] > self.uf[q_root]:
self.uf[q_root] += self.uf[p_root]
self.uf[p_root] = q_root
else:
self.uf[p_root] += self.uf[q_root]
self.uf[q_root] = p_root
self.sets_count -= 1

def is_connected(self, p, q):
return self.find(p) == self.find(q)


class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = defaultdict(list)
self.uf = UnionFind(vertices)

def addEdge(self, u, v):
self.graph[u].append(v)

def isCyclic(self):
for i in self.graph:
for j in self.graph[i]:
if self.uf.is_connected(i, j):
return True
self.uf.union(i, j)


def main():
g = Graph(3)
g.addEdge(0, 1)
g.addEdge(1, 2)
g.addEdge(2, 0)
print(g.isCyclic())


if __name__ == '__main__':
main()