Graph Algorithms: Depth-First Search, Bellman-Ford, and Euler
Depth-First Search (DFS)
Code:
def _dfs(G, node, verts):
# Add node to visited
verts.add(node)
# For each neighbor
for neigh in G[node]:
# If neighbor not visited
if neigh not in verts:
# Start exploration from neighbor
verts = _dfs(G, neigh, verts)
return verts
Connected Components
Code:
def cnx(G):
components = []
visited = set()
# For every node (so we guarantee that every node is visited even if not connected)
for node in G:
if node in visited: continue # If already visited, next
# Explore (e.g. with DFS) from the given node
comp = _dfs(G, node, set())
# Update visited from that exploration
visited.update(comp)
# If DFS finished, a connected component has already been found
components.append(list(comp))
return components
Bellman-Ford Algorithm
Code:
def bellman_ford(G, origin, destination):
distance = dict()
parent = dict()
# Initialize structures
for node in G.nodes:
distance[node] = float('inf')
parent[node] = None
distance[origin] = 0
# Relax the edges node-1 times
for _ in range(len(G)-1):
for u,v in G.edges:
new_dist = G[u][v]['weight']
if distance[u] + new_dist < distance[v]:
distance[v] = distance[u] + new_dist
parent[v] = u
# If when trying to relax the edges one more time we succeed, there is a negative cycle
for u,v in G.edges:
new_dist = G[u][v]['weight']
if distance[u] + new_dist < distance[v]:
print("Negative cycle detected!")
return {
'path': [],
'distance': float('inf')
}
# Reconstruct the path
path = []
node = destination
while node != None:
path.append(node)
node = parent[node]
if path[0] != destination or path[-1] != origin:
print("The destination node is not reachable from the origin")
return {'path': [],
'distance': float('inf')}
return {
'path': path[::-1],
'distance': distance[destination]
}
Euler Algorithm
from random import choice
Code:
def euler(G):
# Check if has euler cycle
for node in G.nodes:
if G.degree(node) % 2: return []
# Visited EDGES
visited = set()
# Random starting node
cycles = []
path = [choice(list(G))]
while len(visited) is not len(G.edges):
node = path[-1]
for neigh in G.neighbors(node):
edge = tuple(sorted([node, neigh]))
# Pick unvisited edge
if edge not in visited:
path.append(neigh)
visited.add(edge)
break
# Closed loop
if path[0] == path[-1]:
cycles.append(path)
# Search next node on path
for node in path:
edges = set([tuple(sorted([node, n])) for n in G.neighbors(node)])
unvisited = edges - visited
if unvisited:
path = [node]
break
# Reconstruct path from cycles
path = []
for c in cycles:
if not path: path = c # First
else:
idx = path.index(c[0])
path = path[:idx] + c + path[idx+1:]
return path