Skip to content

Commit ecad94b

Browse files
committed
[Feature] Added Dijkstras Pathfinding algorithm for weighted directed graphs.
1 parent c11aa91 commit ecad94b

File tree

5 files changed

+165
-1
lines changed

5 files changed

+165
-1
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ $ coverage report -m
3636
* [2D Grid BFS](searches/breadth_first_search.py)
3737
* [Graph BFS](searches/breadth_first_search.py)
3838
* [Finding Duplicates](searches/find_duplicates.py)
39+
* [Dijkstra's Pathfinding](searches/dijkstras.py)
3940

4041
### Data Structures
4142
* [LRU Cache](structures/lru_cache.py)
@@ -56,6 +57,8 @@ $ coverage report -m
5657
* [Find Start of Cycle](structures/linked_list.py)
5758
* [Graph](structures/graph.py)
5859
* [Detect Cycle](structures/graph.py)
60+
* [Directed](structures/graph.py)
61+
* [Directed Weighted](structures/graph.py)
5962
* [Fenwick Tree](structures/fenwick_tree.py)
6063

6164
### Algorithms

searches/dijkstras.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from functools import reduce
2+
3+
def dijkstras(weighted_graph,src,dst):
4+
5+
vertices = set()
6+
dist = {}
7+
prev = {}
8+
9+
# add all vertices to set
10+
for vertex in weighted_graph.vertices:
11+
vertices.add(vertex)
12+
dist[vertex] = float('inf')
13+
prev[vertex] = None
14+
15+
dist[src] = 0
16+
17+
while vertices:
18+
min_dist_node = min(list(filter(lambda x: x[0] in vertices,dist.items())),key=lambda x: x[1])[0]
19+
20+
if min_dist_node == dst:
21+
break
22+
23+
vertices.remove(min_dist_node)
24+
25+
for neighbor in weighted_graph.vertices[min_dist_node].adjacent:
26+
27+
distance = weighted_graph.vertices[min_dist_node].adjacent[neighbor]
28+
29+
if distance < dist[neighbor]:
30+
dist[neighbor] = distance
31+
prev[neighbor] = min_dist_node
32+
33+
# trace back from dst
34+
path = []
35+
current = dst
36+
while prev[current]:
37+
path.append(current)
38+
current = prev[current]
39+
path.append(current)
40+
41+
return path[::-1]
42+

structures/graph.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,45 @@ def dfs(vertex):
111111
dfs(i)
112112

113113
return stack
114-
114+
115+
class WeightedGraphNode():
116+
117+
def __init__(self, val):
118+
self.val = val
119+
self.adjacent = {}
120+
121+
def __str__(self):
122+
return str(self.val) + ' adjacent: ' + str([x for x in self.adjacent])
123+
124+
def add_adjacent(self, node, weight=0):
125+
self.adjacent[node] = weight
126+
127+
def remove_adjacent(self, node):
128+
del self.adjacent[node]
129+
130+
class WeightedGraph():
131+
132+
def __init__(self):
133+
self.vertices = {}
134+
135+
def __str__(self):
136+
return ", ".join([node.__str__() for node in self.vertices.values()])
137+
138+
def add_vertex(self, val):
139+
vertex = WeightedGraphNode(val)
140+
self.vertices[val] = vertex
141+
142+
def add_edge(self, src, dst, weight=0):
143+
if src not in self.vertices:
144+
self.add_vertex(src)
145+
if dst not in self.vertices:
146+
self.add_vertex(dst)
147+
self.vertices[src].add_adjacent(dst, weight)
148+
149+
def remove_edge(self, src, dst):
150+
if src not in self.vertices:
151+
return
152+
if dst not in self.vertices:
153+
return
154+
if dst in self.vertices[src].adjacent:
155+
self.vertices[src].remove_adjacent(dst)

tests/test_dijkstras.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import unittest
2+
from searches.dijkstras import dijkstras
3+
from structures.graph import WeightedGraph
4+
5+
class TestDijkstras(unittest.TestCase):
6+
7+
def test_basic_dij(self):
8+
graph = WeightedGraph()
9+
10+
graph.add_edge('a','b',1)
11+
graph.add_edge('a','c',4)
12+
graph.add_edge('b','c',2)
13+
14+
self.assertEqual(['a','b','c'], dijkstras(graph,'a','c'))
15+
16+
def test_advanced_dij(self):
17+
graph = WeightedGraph()
18+
19+
graph.add_edge('1','2',7)
20+
graph.add_edge('1','3',9)
21+
graph.add_edge('1','6',14)
22+
graph.add_edge('2','3',10)
23+
graph.add_edge('2','4',15)
24+
graph.add_edge('3','4',11)
25+
graph.add_edge('3','6',2)
26+
graph.add_edge('4','5',6)
27+
graph.add_edge('6','5',9)
28+
29+
self.assertEqual(['1','3','6','5'], dijkstras(graph,'1','5'))

tests/test_weighted_graph.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import unittest
2+
from structures.graph import WeightedGraphNode, WeightedGraph
3+
4+
class TestWeightedGraph(unittest.TestCase):
5+
6+
def test_add_vertex(self):
7+
8+
graph = WeightedGraph()
9+
10+
graph.add_vertex('a')
11+
graph.add_vertex('b')
12+
graph.add_vertex('c')
13+
14+
self.assertEqual(['a','b','c'], list(graph.vertices.keys()))
15+
16+
def test_add_edge(self):
17+
18+
graph = WeightedGraph()
19+
20+
graph.add_edge('a','b',1)
21+
graph.add_edge('a','c',3)
22+
graph.add_edge('b','c',2)
23+
24+
self.assertEqual({'b':1,'c':3}, graph.vertices['a'].adjacent)
25+
26+
self.assertEqual({'c':2}, graph.vertices['b'].adjacent)
27+
28+
def test_remove_adjacent(self):
29+
30+
graph = WeightedGraph()
31+
32+
graph.add_edge('a','b',2)
33+
34+
graph.remove_edge('a','b')
35+
36+
graph.remove_edge('a','c')
37+
38+
graph.remove_edge('c','a')
39+
40+
self.assertEqual({}, graph.vertices['a'].adjacent)
41+
42+
def test_print_graph(self):
43+
graph = WeightedGraph()
44+
45+
graph.add_edge('a','b',1)
46+
graph.add_edge('a','c',3)
47+
graph.add_edge('b','c',2)
48+
49+
self.assertEqual("a adjacent: ['b', 'c'], b adjacent: ['c'], c adjacent: []", graph.__str__())

0 commit comments

Comments
 (0)