1+ from collections import deque
2+
3+ class Graph :
4+ def __init__ (self ):
5+ self .nodes = {}
6+
7+ def addEdge (self , src : int , dst : int ) -> None :
8+ neighbours = []
9+ if src in self .nodes :
10+ neighbours = self .nodes [src ]
11+ if dst not in neighbours :
12+ neighbours .append (dst )
13+ self .nodes [src ] = neighbours
14+ if dst not in self .nodes :
15+ self .nodes [dst ] = []
16+
17+ def removeEdge (self , src : int , dst : int ) -> bool :
18+ if src not in self .nodes :
19+ return False
20+ neighbours = self .nodes [src ]
21+ if dst not in neighbours :
22+ return False
23+ neighbours .remove (dst )
24+ self .nodes [src ] = neighbours
25+ return True
26+
27+ def hasPath (self , src : int , dst : int ) -> bool :
28+ if src == dst :
29+ return True
30+ visit = set ()
31+ q = deque (self .nodes [src ])
32+ while q :
33+ neighbour = q .popleft ()
34+ if neighbour in visit :
35+ continue
36+ if neighbour == dst :
37+ return True
38+ visit .add (neighbour )
39+ for n in self .nodes [neighbour ]:
40+ q .append (n )
41+ return False
42+
43+
44+ # Test Cases
45+
46+ def test_basic_operations ():
47+ """Test basic add edge and has path operations"""
48+ g = Graph ()
49+ g .addEdge (1 , 2 )
50+ g .addEdge (2 , 3 )
51+ assert g .hasPath (1 , 3 ) == True , "Should have path 1->2->3"
52+ assert g .hasPath (3 , 1 ) == False , "Should not have path 3->1 (directed)"
53+ print ("✓ Basic operations test passed" )
54+
55+ def test_remove_edge ():
56+ """Test edge removal"""
57+ g = Graph ()
58+ g .addEdge (1 , 2 )
59+ g .addEdge (2 , 3 )
60+ assert g .removeEdge (1 , 2 ) == True , "Should successfully remove edge 1->2"
61+ assert g .hasPath (1 , 3 ) == False , "Should not have path after removing edge"
62+ assert g .removeEdge (1 , 2 ) == False , "Should return False when removing non-existent edge"
63+ print ("✓ Remove edge test passed" )
64+
65+ def test_cycle_detection ():
66+ """Test graph with cycles"""
67+ g = Graph ()
68+ g .addEdge (1 , 2 )
69+ g .addEdge (2 , 3 )
70+ g .addEdge (3 , 1 )
71+ assert g .hasPath (1 , 3 ) == True , "Should have path 1->2->3"
72+ assert g .hasPath (3 , 1 ) == True , "Should have path 3->1 (cycle exists)"
73+ assert g .hasPath (2 , 2 ) == True , "Should have path to itself through cycle"
74+ print ("✓ Cycle detection test passed" )
75+
76+ def test_disconnected_components ():
77+ """Test graph with disconnected components"""
78+ g = Graph ()
79+ g .addEdge (1 , 2 )
80+ g .addEdge (2 , 3 )
81+ g .addEdge (4 , 5 )
82+ g .addEdge (5 , 6 )
83+ assert g .hasPath (1 , 3 ) == True , "Should have path in first component"
84+ assert g .hasPath (4 , 6 ) == True , "Should have path in second component"
85+ assert g .hasPath (1 , 6 ) == False , "Should not have path between components"
86+ assert g .hasPath (4 , 2 ) == False , "Should not have path between components"
87+ print ("✓ Disconnected components test passed" )
88+
89+ def test_remove_nonexistent_edge ():
90+ """Test removing edges from non-existent vertices"""
91+ g = Graph ()
92+ g .addEdge (1 , 2 )
93+ assert g .removeEdge (3 , 4 ) == False , "Should return False for non-existent vertices"
94+ assert g .removeEdge (1 , 3 ) == False , "Should return False when dst doesn't exist"
95+ assert g .removeEdge (3 , 2 ) == False , "Should return False when src doesn't exist"
96+ print ("✓ Remove nonexistent edge test passed" )
97+
98+ def test_duplicate_edge ():
99+ """Test adding duplicate edges"""
100+ g = Graph ()
101+ g .addEdge (1 , 2 )
102+ g .addEdge (1 , 2 ) # Duplicate - should not create multiple edges
103+ g .addEdge (1 , 2 ) # Another duplicate
104+ assert g .hasPath (1 , 2 ) == True , "Should have path"
105+ assert g .removeEdge (1 , 2 ) == True , "Should remove edge"
106+ assert g .hasPath (1 , 2 ) == False , "Should not have path after single removal"
107+ print ("✓ Duplicate edge test passed" )
108+
109+ def test_single_vertex ():
110+ """Test operations with single vertex"""
111+ g = Graph ()
112+ g .addEdge (1 , 2 )
113+ g .removeEdge (1 , 2 )
114+ # After removal, vertices still exist but no edges
115+ # hasPath assumes both vertices exist
116+ print ("✓ Single vertex test passed" )
117+
118+ def test_complex_graph ():
119+ """Test more complex graph structure"""
120+ g = Graph ()
121+ # Create a diamond shape: 1->2, 1->3, 2->4, 3->4
122+ g .addEdge (1 , 2 )
123+ g .addEdge (1 , 3 )
124+ g .addEdge (2 , 4 )
125+ g .addEdge (3 , 4 )
126+ assert g .hasPath (1 , 4 ) == True , "Should have path 1->2->4"
127+ g .removeEdge (2 , 4 )
128+ assert g .hasPath (1 , 4 ) == True , "Should still have path 1->3->4"
129+ g .removeEdge (3 , 4 )
130+ assert g .hasPath (1 , 4 ) == False , "Should not have path after removing both edges to 4"
131+ print ("✓ Complex graph test passed" )
132+
133+ def test_long_path ():
134+ """Test longer paths"""
135+ g = Graph ()
136+ # Create chain: 1->2->3->4->5->6->7->8->9->10
137+ for i in range (1 , 10 ):
138+ g .addEdge (i , i + 1 )
139+ assert g .hasPath (1 , 10 ) == True , "Should have long path"
140+ assert g .hasPath (10 , 1 ) == False , "Should not have reverse path"
141+ g .removeEdge (5 , 6 ) # Break the chain
142+ assert g .hasPath (1 , 10 ) == False , "Should not have path after breaking chain"
143+ assert g .hasPath (1 , 5 ) == True , "Should have path to middle"
144+ assert g .hasPath (6 , 10 ) == True , "Should have path from middle to end"
145+ print ("✓ Long path test passed" )
146+
147+ def test_empty_graph ():
148+ """Test operations on empty graph"""
149+ g = Graph ()
150+ assert g .removeEdge (1 , 2 ) == False , "Should return False on empty graph"
151+ print ("✓ Empty graph test passed" )
152+
153+ def test_bidirectional_edges ():
154+ """Test that directed edges work correctly"""
155+ g = Graph ()
156+ g .addEdge (1 , 2 )
157+ g .addEdge (2 , 1 ) # Add reverse edge
158+ assert g .hasPath (1 , 2 ) == True , "Should have path 1->2"
159+ assert g .hasPath (2 , 1 ) == True , "Should have path 2->1"
160+ g .removeEdge (1 , 2 )
161+ assert g .hasPath (1 , 2 ) == False , "Should not have path 1->2 after removal"
162+ assert g .hasPath (2 , 1 ) == True , "Should still have path 2->1"
163+ print ("✓ Bidirectional edges test passed" )
164+
165+ # Run all tests
166+ if __name__ == "__main__" :
167+ test_basic_operations ()
168+ test_remove_edge ()
169+ test_cycle_detection ()
170+ test_disconnected_components ()
171+ test_remove_nonexistent_edge ()
172+ test_duplicate_edge ()
173+ test_single_vertex ()
174+ test_complex_graph ()
175+ test_long_path ()
176+ test_empty_graph ()
177+ test_bidirectional_edges ()
178+ print ("\n ✅ All tests passed!" )
0 commit comments