Skip to content

Commit d55d809

Browse files
Added BCC and binary lifting
1 parent 7f54a87 commit d55d809

File tree

2 files changed

+158
-0
lines changed

2 files changed

+158
-0
lines changed

pyrival/graphs/bcc.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""
2+
Given a directed graph, find_SCC returns a list of lists containing
3+
the strongly connected components in topological order.
4+
5+
Note that this implementation can be also be used to check if a directed graph is a
6+
DAG, and in that case it can be used to find the topological ordering of the nodes.
7+
"""
8+
9+
def find_SCC(graph):
10+
SCC, S, P = [], [], []
11+
depth = [0] * len(graph)
12+
13+
stack = list(range(len(graph)))
14+
while stack:
15+
node = stack.pop()
16+
if node < 0:
17+
d = depth[~node] - 1
18+
if P[-1] > d:
19+
SCC.append(S[d:])
20+
del S[d:], P[-1]
21+
for node in SCC[-1]:
22+
depth[node] = -1
23+
elif depth[node] > 0:
24+
while P[-1] > depth[node]:
25+
P.pop()
26+
elif depth[node] == 0:
27+
S.append(node)
28+
P.append(len(S))
29+
depth[node] = len(S)
30+
stack.append(~node)
31+
stack += graph[node]
32+
return SCC[::-1]
33+
34+
"""
35+
Given an undirected simple graph, find_BCC returns a list of lists
36+
containing the biconnected components of the graph. Runs in O(n + m) time.
37+
"""
38+
def find_BCC(graph):
39+
d = 0
40+
depth = [0] * len(graph)
41+
stack = list(range(len(graph)))
42+
while stack:
43+
node = stack.pop()
44+
if node < 0:
45+
d -= 1
46+
elif not depth[node]:
47+
d = depth[node] = d + 1
48+
stack.append(~node)
49+
stack += graph[node]
50+
51+
graph2 = [[v for v in g if d + 2 > depth[v] != d - 1] for g,d in zip(graph, depth)]
52+
return find_SCC(graph2)
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"""
2+
Binary lifting technique applied to a tree.
3+
There are three different uses of this implementation
4+
5+
1. Computing LCA in O(log n) time.
6+
7+
Example:
8+
0
9+
|
10+
1
11+
/ \
12+
2 3
13+
14+
graph = [[1], [0, 2, 3], [1], [1]]
15+
BL = binary_lift(graph, root=0)
16+
print(BL.lca(2, 3)) # prints 1
17+
18+
2. Compute the distance between two nodes in O(log n) time.
19+
20+
Example:
21+
graph = [[1], [0, 2, 3], [1], [1]]
22+
BL = binary_lift(graph)
23+
print(BL.distance(2, 3)) # prints 2
24+
25+
3. Compute the sum/min/max/... of the weight
26+
of a path between a pair of nodes in O(log n) time.
27+
28+
res = Path[0]
29+
for node in Path[1:]:
30+
res = f(res, node)
31+
return res
32+
33+
Example:
34+
35+
graph = [[1], [0, 2, 3], [1], [1]]
36+
data = [1, 10, 20, 5]
37+
BL = binary_lift(graph, data, f = lambda a,b: a + b)
38+
print(BL(2, 3)) # prints 35
39+
"""
40+
41+
class binary_lift:
42+
def __init__(self, graph, data=(), f=min, root=0):
43+
n = len(graph)
44+
45+
parent = [-1] * (n + 1)
46+
depth = self.depth = [-1] * n
47+
bfs = [root]
48+
depth[root] = 0
49+
for node in bfs:
50+
for nei in graph[node]:
51+
if depth[nei] == -1:
52+
parent[nei] = node
53+
depth[nei] = depth[node] + 1
54+
bfs.append(nei)
55+
56+
data = self.data = [data]
57+
parent = self.parent = [parent]
58+
self.f = f
59+
60+
for _ in range(max(depth).bit_length()):
61+
old_data = data[-1]
62+
old_parent = parent[-1]
63+
64+
data.append([f(val, old_data[p]) for val,p in zip(old_data, old_parent)])
65+
parent.append([old_parent[p] for p in old_parent])
66+
67+
def lca(self, a, b):
68+
depth = self.depth
69+
parent = self.parent
70+
71+
if depth[a] < depth[b]:
72+
a,b = b,a
73+
74+
d = depth[a] - depth[b]
75+
for i in range(d.bit_length()):
76+
if (d >> i) & 1:
77+
a = parent[i][a]
78+
79+
for i in range(depth[a].bit_length())[::-1]:
80+
if parent[i][a] != parent[i][b]:
81+
a = parent[i][a]
82+
b = parent[i][b]
83+
84+
if a != b:
85+
return parent[0][a]
86+
else:
87+
return a
88+
89+
def distance(self, a, b):
90+
return self.depth[a] + self.depth[b] - 2 * self.depth[self.lca(a,b)]
91+
92+
def __call__(self, a, b):
93+
depth = self.depth
94+
parent = self.parent
95+
data = self.data
96+
f = self.f
97+
98+
c = self.lca(a, b)
99+
val = data[0][c]
100+
for x,d in (a, depth[a] - depth[c]), (b, depth[b] - depth[c]):
101+
for i in range(d.bit_length()):
102+
if (d >> i) & 1:
103+
val = f(val, data[i][x])
104+
x = parent[i][x]
105+
106+
return val

0 commit comments

Comments
 (0)