1
+ from queue import Queue
2
+
3
+ class Graph :
4
+ # Constructor
5
+ def __init__ (self , num_of_nodes , directed = True ):
6
+ self .m_num_of_nodes = num_of_nodes
7
+ self .m_nodes = range (self .m_num_of_nodes )
8
+
9
+ # Directed or Undirected
10
+ self .m_directed = directed
11
+
12
+ # Graph representation - Adjacency list
13
+ # We use a dictionary to implement an adjacency list
14
+ self .m_adj_list = {node : set () for node in self .m_nodes }
15
+
16
+ # Add edge to the graph
17
+ def add_edge (self , node1 , node2 , weight = 1 ):
18
+ self .m_adj_list [node1 ].add ((node2 , weight ))
19
+
20
+ if not self .m_directed :
21
+ self .m_adj_list [node2 ].add ((node1 , weight ))
22
+
23
+ # Print the graph representation
24
+ def print_adj_list (self ):
25
+ for key in self .m_adj_list .keys ():
26
+ print ("node" , key , ": " , self .m_adj_list [key ])
27
+
28
+ def bfs (self , start_node , target_node ):
29
+ # Set of visited nodes to prevent loops
30
+ visited = set ()
31
+ queue = Queue ()
32
+
33
+ # Add the start_node to the queue and visited list
34
+ queue .put (start_node )
35
+ visited .add (start_node )
36
+
37
+ # start_node has not parents
38
+ parent = dict ()
39
+ parent [start_node ] = None
40
+
41
+ # Perform step 3
42
+ path_found = False
43
+ while not queue .empty ():
44
+ current_node = queue .get ()
45
+ if current_node == target_node :
46
+ path_found = True
47
+ break
48
+
49
+ for (next_node , weight ) in self .m_adj_list [current_node ]:
50
+ if next_node not in visited :
51
+ queue .put (next_node )
52
+ parent [next_node ] = current_node
53
+ visited .add (next_node )
54
+
55
+ # Path reconstruction
56
+ path = []
57
+ if path_found :
58
+ path .append (target_node )
59
+ while parent [target_node ] is not None :
60
+ path .append (parent [target_node ])
61
+ target_node = parent [target_node ]
62
+ path .reverse ()
63
+ return path
64
+
65
+ def bfs_traversal (self , start_node ):
66
+ visited = set ()
67
+ queue = Queue ()
68
+ queue .put (start_node )
69
+ visited .add (start_node )
70
+
71
+ while not queue .empty ():
72
+ current_node = queue .get ()
73
+ print (current_node , end = " " )
74
+ for (next_node , weight ) in self .m_adj_list [current_node ]:
75
+ if next_node not in visited :
76
+ queue .put (next_node )
77
+ visited .add (next_node )
78
+
79
+ def main ():
80
+ # Testing the `bfs()` method
81
+ graph1 = Graph (6 , directed = False )
82
+
83
+ graph1 .add_edge (0 , 1 )
84
+ graph1 .add_edge (0 , 2 )
85
+ graph1 .add_edge (0 , 3 )
86
+ graph1 .add_edge (0 , 4 )
87
+ graph1 .add_edge (1 , 2 )
88
+ graph1 .add_edge (2 , 3 )
89
+ graph1 .add_edge (2 , 5 )
90
+ graph1 .add_edge (3 , 4 )
91
+ graph1 .add_edge (3 , 5 )
92
+ graph1 .add_edge (4 , 5 )
93
+
94
+ graph1 .print_adj_list ()
95
+
96
+ path = []
97
+ path = graph1 .bfs (0 , 5 )
98
+ print (path )
99
+
100
+ # Testing the `bfs_traversal()` method
101
+ graph2 = Graph (5 , directed = False )
102
+
103
+ graph2 .add_edge (0 , 1 )
104
+ graph2 .add_edge (0 , 2 )
105
+ graph2 .add_edge (1 , 2 )
106
+ graph2 .add_edge (1 , 4 )
107
+ graph2 .add_edge (2 , 3 )
108
+
109
+ graph2 .bfs_traversal (0 )
110
+
111
+ if __name__ == "__main__" :
112
+ main ()
0 commit comments