Skip to content

Commit a8ff164

Browse files
Merge pull request #231 from Pradeepsingh61/add-python-graph-bfs-dfs
Add Graph Traversal algorithms (BFS and DFS) in Python
2 parents e8b0e98 + 37b7bae commit a8ff164

1 file changed

Lines changed: 209 additions & 0 deletions

File tree

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
"""
2+
Graph Traversal Algorithms - BFS and DFS
3+
Time Complexity: O(V + E) where V = vertices, E = edges
4+
Space Complexity: O(V) for visited set and queue/stack
5+
Author: Karanjot Singh
6+
Date: October 2025
7+
Hacktoberfest 2025
8+
"""
9+
10+
from collections import deque, defaultdict
11+
12+
13+
class Graph:
14+
"""Graph representation using adjacency list"""
15+
16+
def __init__(self):
17+
self.graph = defaultdict(list)
18+
19+
def add_edge(self, u, v):
20+
"""
21+
Add an edge to the graph (undirected)
22+
23+
Args:
24+
u: Source vertex
25+
v: Destination vertex
26+
"""
27+
self.graph[u].append(v)
28+
self.graph[v].append(u)
29+
30+
def add_directed_edge(self, u, v):
31+
"""
32+
Add a directed edge to the graph
33+
34+
Args:
35+
u: Source vertex
36+
v: Destination vertex
37+
"""
38+
self.graph[u].append(v)
39+
40+
def bfs(self, start):
41+
"""
42+
Breadth-First Search traversal
43+
44+
Visits nodes level by level using a queue
45+
Useful for: shortest path, level-order traversal
46+
47+
Args:
48+
start: Starting vertex
49+
50+
Returns:
51+
List of vertices in BFS order
52+
"""
53+
visited = set()
54+
queue = deque([start])
55+
visited.add(start)
56+
result = []
57+
58+
while queue:
59+
vertex = queue.popleft()
60+
result.append(vertex)
61+
62+
# Visit all adjacent vertices
63+
for neighbor in self.graph[vertex]:
64+
if neighbor not in visited:
65+
visited.add(neighbor)
66+
queue.append(neighbor)
67+
68+
return result
69+
70+
def dfs(self, start):
71+
"""
72+
Depth-First Search traversal (iterative)
73+
74+
Visits nodes by going deep into branches using a stack
75+
Useful for: path finding, cycle detection, topological sort
76+
77+
Args:
78+
start: Starting vertex
79+
80+
Returns:
81+
List of vertices in DFS order
82+
"""
83+
visited = set()
84+
stack = [start]
85+
result = []
86+
87+
while stack:
88+
vertex = stack.pop()
89+
90+
if vertex not in visited:
91+
visited.add(vertex)
92+
result.append(vertex)
93+
94+
# Add neighbors to stack (reverse order for consistent traversal)
95+
for neighbor in reversed(self.graph[vertex]):
96+
if neighbor not in visited:
97+
stack.append(neighbor)
98+
99+
return result
100+
101+
def dfs_recursive(self, start, visited=None):
102+
"""
103+
Depth-First Search traversal (recursive)
104+
105+
Args:
106+
start: Starting vertex
107+
visited: Set of visited vertices (used internally)
108+
109+
Returns:
110+
List of vertices in DFS order
111+
"""
112+
if visited is None:
113+
visited = set()
114+
115+
visited.add(start)
116+
result = [start]
117+
118+
for neighbor in self.graph[start]:
119+
if neighbor not in visited:
120+
result.extend(self.dfs_recursive(neighbor, visited))
121+
122+
return result
123+
124+
def is_connected(self, start):
125+
"""
126+
Check if all vertices are reachable from start
127+
128+
Args:
129+
start: Starting vertex
130+
131+
Returns:
132+
True if graph is connected, False otherwise
133+
"""
134+
visited = set()
135+
self._dfs_visit(start, visited)
136+
return len(visited) == len(self.graph)
137+
138+
def _dfs_visit(self, vertex, visited):
139+
"""Helper method for connectivity check"""
140+
visited.add(vertex)
141+
for neighbor in self.graph[vertex]:
142+
if neighbor not in visited:
143+
self._dfs_visit(neighbor, visited)
144+
145+
146+
# Test cases
147+
if __name__ == "__main__":
148+
print("=== Graph Traversal - BFS and DFS ===\n")
149+
150+
# Test case 1: Simple undirected graph
151+
print("--- Test Case 1: Undirected Graph ---")
152+
g1 = Graph()
153+
edges1 = [(0, 1), (0, 2), (1, 3), (1, 4), (2, 5), (2, 6)]
154+
155+
print(f"Adding edges: {edges1}")
156+
for u, v in edges1:
157+
g1.add_edge(u, v)
158+
159+
print(f"BFS from vertex 0: {g1.bfs(0)}")
160+
print(f"DFS from vertex 0 (iterative): {g1.dfs(0)}")
161+
print(f"DFS from vertex 0 (recursive): {g1.dfs_recursive(0)}")
162+
163+
# Test case 2: Directed graph
164+
print("\n--- Test Case 2: Directed Graph ---")
165+
g2 = Graph()
166+
directed_edges = [(0, 1), (0, 2), (1, 3), (2, 3), (3, 4)]
167+
168+
print(f"Adding directed edges: {directed_edges}")
169+
for u, v in directed_edges:
170+
g2.add_directed_edge(u, v)
171+
172+
print(f"BFS from vertex 0: {g2.bfs(0)}")
173+
print(f"DFS from vertex 0 (iterative): {g2.dfs(0)}")
174+
175+
# Test case 3: Larger graph
176+
print("\n--- Test Case 3: Larger Graph ---")
177+
g3 = Graph()
178+
edges3 = [(0, 1), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (3, 4)]
179+
180+
print(f"Adding edges: {edges3}")
181+
for u, v in edges3:
182+
g3.add_edge(u, v)
183+
184+
print(f"BFS from vertex 0: {g3.bfs(0)}")
185+
print(f"DFS from vertex 0: {g3.dfs(0)}")
186+
187+
# Test case 4: Linear graph
188+
print("\n--- Test Case 4: Linear Graph ---")
189+
g4 = Graph()
190+
edges4 = [(1, 2), (2, 3), (3, 4), (4, 5)]
191+
192+
print(f"Adding edges: {edges4}")
193+
for u, v in edges4:
194+
g4.add_edge(u, v)
195+
196+
print(f"BFS from vertex 1: {g4.bfs(1)}")
197+
print(f"DFS from vertex 1: {g4.dfs(1)}")
198+
199+
# Test case 5: Disconnected graph check
200+
print("\n--- Test Case 5: Connectivity Check ---")
201+
g5 = Graph()
202+
g5.add_edge(0, 1)
203+
g5.add_edge(1, 2)
204+
g5.add_edge(3, 4) # Disconnected component
205+
206+
print(f"Graph with vertices: 0-1-2 and 3-4")
207+
print(f"Is connected from 0? {g5.is_connected(0)}")
208+
209+
print("\n✅ All test cases completed!")

0 commit comments

Comments
 (0)