Skip to content
Permalink
Browse files
Suggested updates to PFAB#6: Breadth-first search
  • Loading branch information
robert committed Nov 23, 2019
1 parent f1acc92 commit 80041bdfe61502d85a3ca4fbf8a46875422ed6ed
Showing 1 changed file with 81 additions and 67 deletions.
@@ -1,5 +1,6 @@
from __future__ import print_function
from prettytable import PrettyTable
import copy


def adjacency_list(graph):
@@ -14,7 +15,24 @@ def adjacency_list(graph):
return adj_list


def bfs_traversal(graph, source_vertex):
def load_graph_from_file(filename):
with open(r'{}'.format(filename), 'r') as f:
graph = {}
for edge in f:
edge = edge.split()
graph.setdefault(edge[0], []).append(edge[1])
return _preprocess_graph(graph)


def distances(graph, source_vertex):
return traverse(graph, source_vertex)[0]


def traversal_path(graph, source_vertex):
return traverse(graph, source_vertex)[1]


def traverse(graph, source_vertex):
bfs_path = []
distances = dict.fromkeys(graph, 0) # Set all edges distances to zero
queue = [str(source_vertex)] # Enqueue the source vertex
@@ -28,42 +46,8 @@ def bfs_traversal(graph, source_vertex):
queue.append(neighbor)
visited[neighbor] = True
distances[neighbor] = distances[dequeue] + 1
print(f"BFS traversal order (starting from vertex {source_vertex}): {'->'.join(bfs_path)}")
print("")
return single_source_path_table(distances)


def multi_source_path(graph, source_vertex, destination_vertex, found_w=False):
distance = 0
queue, track = [], []
source_v = [source_vertex]
while not found_w:
for vertex in source_v:
track.append(vertex)
for node in graph[vertex]:
if node not in track:
queue.extend([node])
if destination_vertex in queue:
distance += 1
found_w = True
else:
if not queue: # if queue is empty
distance = 0
found_w = True
else: # if queue is not empty
source_v = set(queue[:])
distance += 1
queue.clear()
return distance


def read_data(filename):
with open(r'{}'.format(filename), 'r') as f:
graph = {}
for edge in f:
edge = edge.split()
graph.setdefault(edge[0], []).append(edge[1])
return graph
return (distances, bfs_path)


def shortest_path(graph, source_vertex, destination_vertex):
@@ -89,55 +73,85 @@ def shortest_path(graph, source_vertex, destination_vertex):
paths.append(min_edges[0])
source_vertex = min_edges[0]
adj_list.clear()
paths = "->".join(paths)
return paths


def single_edge_distance(graph, v, w):
return multi_source_path(graph, v, w)
return _multi_source_distance(graph, v, w)


def single_source_path_table(distances):
distance_table = PrettyTable()
distance_table.field_names = ["Destination Vertex", "Distance (hops)"]
for destination_vertex, distance in distances.items():
distance_table.add_row([destination_vertex, distance])
return distance_table
def _multi_source_distance(graph, source_vertex, destination_vertex, found_w=False):
distance = 0
queue, track = [], []
source_v = [source_vertex]
while not found_w:
for vertex in source_v:
track.append(vertex)
for node in graph[vertex]:
if node not in track:
queue.extend([node])
if destination_vertex in queue:
distance += 1
found_w = True
else:
if not queue: # if queue is empty
distance = 0
found_w = True
else: # if queue is not empty
source_v = set(queue[:])
distance += 1
queue.clear()
return distance


def validate_graph(graph):
def _preprocess_graph(graph):
"""
Takes a graph in the form of a dictionary and verifies that for every edge member there exists a source node.
If no source node is found from existing edge members, one is created with an empty edge [].
:param graph:
:return:
"""
vertices = [vertex for edge in graph.values() for vertex in edge]
graph_copy = copy.deepcopy(graph)
vertices = [vertex for edge in graph_copy.values() for vertex in edge]
for vertex in vertices:
if vertex not in graph.copy().keys():
graph[vertex] = []
return graph
if vertex not in graph_copy.keys():
graph_copy[vertex] = []
return graph_copy


def driver_code():
def main():
filename = 'edges.txt'
try:
source_vertex = '1'
destination_vertex = '10'
graph = read_data(filename)
validate_graph(graph)
print("")
# print(f"Adjacency list (all vertices):\n{adjacency_list(graph)}")
print("")
print(f"Single-source distance from source vertex [{source_vertex}]:\n{bfs_traversal(graph, source_vertex)}")
print(f"\nShortest path from {source_vertex} to {destination_vertex} is ["
f"{shortest_path(graph, source_vertex, destination_vertex)}] at a distance of "
f"{single_edge_distance(graph, source_vertex, destination_vertex)} hop(s).")
except KeyError:
source_vertex = None
print("Source vertex {} not found in graph.".format(source_vertex))
return
source_vertex = '1'
destination_vertex = '10'
graph = load_graph_from_file(filename)
print("")
# print(f"Adjacency list (all vertices):\n{adjacency_list(graph)}")
print("")

def format_path(p):
return "->".join(p)

def format_distances_table(t):
distance_table = PrettyTable()
distance_table.field_names = ["Destination Vertex", "Distance (hops)"]
for destination_vertex, distance in t.items():
distance_table.add_row([destination_vertex, distance])
return distance_table

shortest = shortest_path(graph, source_vertex, destination_vertex)
formatted_shortest = format_path(shortest)

distances, traversal_path = traverse(graph, source_vertex)
formatted_traversal_path = format_path(traversal_path)
formatted_distances = format_distances_table(distances)

print(f"BFS traversal order (starting from vertex {source_vertex}): {formatted_traversal_path}")
print(f"Single-source distances from source vertex [{source_vertex}]:")
print(formatted_distances)
print(f"\nShortest path from {source_vertex} to {destination_vertex} is ["
f"{formatted_shortest}] at a distance of "
f"{single_edge_distance(graph, source_vertex, destination_vertex)} hop(s).")


if __name__ == "__main__":
driver_code()
main()

0 comments on commit 80041bd

Please sign in to comment.