Repository where I mostly put random python scripts.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
3.8 KiB

  1. """
  2. :Author: James Sherratt
  3. :Date: 20/10/2019
  4. :License: MIT
  5. :name: graph.py
  6. """
  7. class Graph:
  8. def __init__(self):
  9. self.vertices = set()
  10. self.edges = set()
  11. def add_vertex(self, vert):
  12. """
  13. Add a vertex to the graph.
  14. :param vert: name of the vertex.
  15. :return: None
  16. """
  17. self.vertices.add(vert)
  18. def add_edge(self, vert1, vert2, directional=False):
  19. """
  20. Add an edge to the graph. The edge will be defined as a simple tuple where:
  21. - The first value is the initial edge.
  22. - The second is the final edge.
  23. - The third is whether the edge is directional.
  24. :param vert1: the start vertex.
  25. :param vert2: the end vertex.
  26. :param directional: whether or not the edge has a direction. Default: False
  27. :return: None
  28. """
  29. self.vertices.add(vert1)
  30. self.vertices.add(vert2)
  31. if (not directional) and (vert1 > vert2):
  32. # swap if not directional to avoid duplicates in the set.
  33. self.edges.add((vert2, vert1, directional))
  34. else:
  35. self.edges.add((vert1, vert2, directional))
  36. def adjacency(self, vert1, vert2):
  37. """
  38. Check if 2 vertices are adjacent (if they exist). Note: if vert1 and vert2
  39. are connected, but directionally from vert2 to vert1, False is returned.
  40. :param vert1: The first vertex to compare.
  41. :param vert2: The second vertex to compare.
  42. :return: True if adjacent, False if not, None if either are not in the set.
  43. """
  44. if (vert1 not in self.vertices) or (vert2 not in self.vertices):
  45. return None
  46. for edge in self.edges:
  47. if (vert1 == edge[0]) and (vert2 == edge[1]):
  48. return True
  49. if (not edge[2]) and ((vert1 == edge[1]) and (vert2 == edge[0])):
  50. return True
  51. return False
  52. def neighbours(self, vert):
  53. """
  54. Get the neighbours of a vertex.
  55. :param vert: name of the vertex.
  56. :return: list of neighbours, or None if the vertex is not in the graph.
  57. """
  58. if vert not in self.vertices:
  59. return None
  60. neighbours = set()
  61. for edge in self.edges:
  62. if vert == edge[0]:
  63. neighbours.add(edge[1])
  64. elif (not edge[2]) and (vert == edge[1]):
  65. neighbours.add(edge[0])
  66. return neighbours
  67. def as_dict(self):
  68. """
  69. Convert the graph to a dictionary where:
  70. - Each key is a vertex.
  71. - Each value is a set of neighbours you can travel to.
  72. :return: dict representing the graph.
  73. """
  74. graph_dict = {v: set() for v in self.vertices}
  75. for edge in self.edges:
  76. graph_dict[edge[0]].add(edge[1])
  77. if not edge[2]:
  78. graph_dict[edge[1]].add(edge[0])
  79. return graph_dict
  80. if __name__ == "__main__":
  81. mygraph = Graph()
  82. mygraph.add_edge("a", "b")
  83. mygraph.add_edge("b", "c")
  84. mygraph.add_edge("b", "d")
  85. mygraph.add_edge("c", "b")
  86. mygraph.add_edge("a", "e", directional=True)
  87. mygraph.add_vertex("z")
  88. print("b neighbours:", mygraph.neighbours("b"))
  89. print("a neighbours:", mygraph.neighbours("a"))
  90. print("q neighbours:", mygraph.neighbours("q"))
  91. print("e neighbours:", mygraph.neighbours("e"))
  92. print()
  93. # Adjacency has direction.
  94. print("a and e adjacent:", mygraph.adjacency("a", "e"))
  95. print("e and a adjacent:", mygraph.adjacency("e", "a"))
  96. print("d and b adjacent", mygraph.adjacency("d", "b"))
  97. print("q and a adjacent:", mygraph.adjacency("q", "a"))
  98. print("z and a adjacent:", mygraph.adjacency("z", "a"))
  99. print()
  100. print("as dict")
  101. print(mygraph.as_dict())
  102. # Exercise/ project: add a method "path" to find path between 2 vertices.
  103. # (Hint: lookup DFS/ BFS algorithm.)