Source code for tryalgo.shortest_cycle
#!/usr/bin/env python3
"""\
Find shortest simple cycle
christoph durr, finn voelkel and louis abraham - 2016-2019
O(V*E)
footnote (1) here you can add parity check of cycle_len
if only even cycles are requested
"""
# pylint: disable=bad-whitespace, missing-docstring, multiple-statements
from collections import deque
from sys import stdin
from tryalgo.floyd_warshall import floyd_warshall
def readstr(): return stdin.readline().strip()
def readints(): return map(int, stdin.readline().split())
def readint(): return int(stdin.readline())
__all__ = ["shortest_cycle", "powergraph"]
def bfs(graph, root, prune_level):
"""make a pruned BFS search of the graph starting at root.
returns the BFS tree, and possibly a traversal edge (u,v)
that with the tree forms a cycle of some length.
:param graph: undirected graph in listlist or listdict format
:param root: vertex where BFS exploration starts
:param prune_level: exploration is done only up to
the prune_level (included)
:complexity: O(V + E)
"""
n = len(graph)
level = [-1] * n # -1 == not seen
tree = [None] * n # pointers to predecessors
to_visit = deque([root]) # queue for BFS
level[root] = 0
tree[root] = root
best_cycle = float('inf') # start with infinity
best_u = None
best_v = None
while to_visit:
u = to_visit.popleft()
if level[u] > prune_level:
break
for v in graph[u]:
if tree[u] == v: # ignore the tree edge
continue
if level[v] == -1: # new vertex - tree edge
level[v] = level[u] + 1
to_visit.append(v)
tree[v] = u
else: # vertex already seen - traversal edge
prune_level = level[v] - 1
cycle_len = level[u] + 1 + level[v]
if cycle_len < best_cycle: # footnote (1)
best_cycle = cycle_len
best_u = u
best_v = v
return tree, best_cycle, best_u, best_v
def path(tree, v):
"""returns the path in the tree from v to the root
Complexity: O(V)
"""
P = []
while not P or P[-1] != v:
P.append(v)
v = tree[v]
return P
[docs]
def shortest_cycle(graph):
""" Finding a shortest cycle in an undirected unweighted graph
:param graph: undirected graph in listlist or listdict format
:returns: None or a list C describing a shortest cycle
:complexity: `O(|V|*|E|)`
"""
best_cycle = float('inf')
best_u = None
best_v = None
best_tree = None
V = list(range(len(graph)))
for root in V:
tree, cycle_len, u, v = bfs(graph, root, best_cycle // 2)
if cycle_len < best_cycle:
best_cycle = cycle_len
best_u = u
best_v = v
best_tree = tree
if best_cycle == float('inf'):
return None # no cycle found
Pu = path(best_tree, best_u) # combine path to make a cycle
Pv = path(best_tree, best_v)
cycle = Pu[::-1] + Pv # last vertex equals first vertex
return cycle[1:] # remove duplicate vertex
[docs]
def powergraph(graph, k):
"""Compute the k-th
`powergraph <https://en.wikipedia.org/wiki/Graph_power>`_
which has an edge u,v for every vertex pair
of distance at most k in the given graph.
:param graph: undirected graph in listlist or listdict format
:param k: non-negative integer.
:complexity: O(V^3)
"""
V = range(len(graph))
# create weight matrix for paths of length 1
M = [[float('inf') for v in V] for u in V]
for u in V:
for v in graph[u]:
M[u][v] = M[v][u] = 1
M[u][u] = 0
floyd_warshall(M)
return [[v for v in V if M[u][v] <= k] for u in V]