123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- # Copyright 2024 Google LLC
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- """
- Credit to Bernd Klein for this graph class.
- http://www.python-course.eu/graphs_python.php
- Modifications by kmisquitta:
- 1) Added support for pretty printing
- 2) Added function to return a vertex's neighbours
- 3) Added support for traversing to a vertex more than once in find_all_paths
- 4) Removed support for adding edges that are sets
- 5) Removed support for multiple edges between two vertices
- 6) Added support for traversing beyond the end vertex in find_all_paths
- 7) Removed many unneeded features
- """
- """ A Python Class
- A simple Python graph class, demonstrating the essential
- facts and functionalities of graphs.
- """
- import pprint
- def is_line_segment_in_path(path, vertex_1, vertex_2):
- for i in range(len(path) - 1):
- if path[i] == vertex_1 and path[i + 1] == vertex_2 \
- or path[i] == vertex_2 and path[i + 1] == vertex_1:
- return True
- return False
- class Graph(object):
- def __init__(self, graph_dict={}):
- """ initializes a graph object """
- self.__graph_dict = graph_dict
- def get_vertices(self):
- """ returns the vertices of a graph """
- return list(self.__graph_dict.keys())
- def get_edges(self):
- """ returns the edges of a graph """
- return self.__generate_edges()
- def get_neighbours(self, vertex):
- """ returns the neighbours of a vertex """
- return list(self.__graph_dict[vertex])
- def add_vertex(self, vertex):
- """ If the vertex "vertex" is not in
- self.__graph_dict, a key "vertex" with an empty
- list as a value is added to the dictionary.
- Otherwise nothing has to be done.
- """
- if vertex not in self.__graph_dict:
- self.__graph_dict[vertex] = []
- def add_edge(self, edge):
- """ assumes that edge is of type tuple or list """
- if len(edge) < 2:
- return
- vertex1 = edge[0]
- vertex2 = edge[1]
- if vertex1 in self.__graph_dict:
- if not (vertex2 in self.get_neighbours(vertex1)):
- self.__graph_dict[vertex1].append(vertex2)
- else:
- self.__graph_dict[vertex1] = [vertex2]
- def __generate_edges(self):
- """ A static method generating the edges of the
- graph "graph". Edges are represented as sets
- with one (a loop back to the vertex) or two
- vertices
- """
- edges = []
- for vertex in self.__graph_dict:
- for neighbour in self.__graph_dict[vertex]:
- if {vertex, neighbour} not in edges:
- edges.append({vertex, neighbour})
- return edges
- def __str__(self):
- res = "vertices: "
- for k in self.__graph_dict:
- res += str(k) + " "
- res += "\nedges: "
- for edge in self.__generate_edges():
- res += str(edge) + " "
- return res
- def find_all_paths(self, start_vertex, end_vertex, path=[]):
- """ Recursive function that finds all paths from the start vertex to the end vertex.
- Starts from the start vertex and traverses through vertices until the end vertex is reached.
- If there are untraversed edges when the end vertex is reached, will continue traversing
- to check for paths back to the end vertex (loops).
- There is no limit to how many times a vertex can be traversed.
- An edge may be traversed only once.
- """
- graph = self.__graph_dict
- paths = []
- path = path + [start_vertex]
- if start_vertex == end_vertex:
- # Check if additional traversals is possible
- neighbours = self.get_neighbours(end_vertex)
- no_possible_traversals = True
- for neighbour in neighbours:
- if not is_line_segment_in_path(path, end_vertex, neighbour):
- no_possible_traversals = False
- break
- if no_possible_traversals: # Base case
- return [path]
- else:
- paths.append(path) # Add current path, continue finding
- if start_vertex not in graph:
- return []
- for vertex in graph[start_vertex]:
- if not is_line_segment_in_path(path, vertex, start_vertex):
- extended_paths = self.find_all_paths(vertex,
- end_vertex,
- path)
- for p in extended_paths:
- paths.append(p)
- return paths
- def prettyprint(self):
- pprint.pprint(self.__graph_dict)
|