A directed graph $G = (V, E)$, also called a digraph, is a set $V$ of vertices and a set $E$ of directed edges, or edges that proceed from a source vertex to a sink vertex. Here's a crude diagram of a directed graph:
(1) ---> (2) ---> (3) <--
\______________________/
Where node 1 has an edge to node 2 and node 3, and node 2 has an edge to node 3.
In [1]:
graph = {"forward" : {}, "reverse" : {}}
Adding the edge from 1 to 2 requires two steps, adding the forward relationship, and adding the reverse.
In [2]:
graph["forward"][1] = [2]
graph["reverse"][2] = [1]
graph
Out[2]:
To add the edge between 1 and 3, we have to be careful, we need to append 3 to the neighbor list of 1.
In [3]:
graph["forward"][1].append(3)
graph["reverse"][3] = [1]
graph
Out[3]:
When can we assign to the neighbor list and when do we have to append? Let's see what happend when we add the edge from 2 to 3.
In [4]:
graph["forward"][2] = [3]
graph["reverse"][3].append(2)
graph
Out[4]:
In [5]:
def make_digraph():
"Make an empty directed graph"
pass
In [6]:
def add_edge(digraph, source, sink):
"Add a directed edge from the source vertex to the sink vertex to the digraph."
pass
In [7]:
graph2 = make_digraph()
add_edge(graph2, 1, 2)
add_edge(graph2, 1, 3)
add_edge(graph2, 2, 3)
graph2
In class we saw that in order to construct Eulerian paths in directed graphs, we need to change the concept of an even or odd vertex. In particular, in a directed graph, a node is even if it has the same number of forward edges leaving it as edges inbound to it. A vertex is odd otherwise. We can write a function to return the number of inbound edges minus the number of outbound edges.
In [8]:
def check_vertex(digraph, vertex):
"Check if a vertex in a digraph is even or odd."
pass
To have an Eulerian path, all verticies except for two (zero for an Eulerian cycle) must be even, The odd verticies (if any) must have one more outbound edge than inbound (for the start vertex) and one more inbound than outbound (for the end vertex)
In [9]:
def find_path(digraph):
"Find an Eulerian path through digraph."
path = []
# Check if G has an Eulerian path or exit
# make a copy of G (from now on work on the copy)
# Choose a starting vertex
# Push starting vertex onto stack
# while items remain on the stack:
# set current vertex to top of stack
# if current vertex has forward (outbound) edges
# put neighbor on stack
# remove edge from current vertex to neighbor
# else
# append current vertex to path
# pop current vertex from stack
return path
You will also need to implement remove_edge
, and functions to check if a path exists, and choose a starting node.
In [10]:
def remove_edge(digraph, source, sink):
pass
In [11]:
def has_path(digraph):
"Test if this digraph has an Eulerian path"
pass
In [12]:
def pick_start(digraph):
"Find a suitable starting vertex in digraph"
pass
In [ ]:
graph3 = make_digraph()
add_edge(graph3, 1, 2)
add_edge(graph3, 2, 3)
add_edge(graph3, 3, 1)
add_edge(graph3, 1, 4)
find_path(graph3)