Skip to content

Commit b003f8d

Browse files
committed
adding dijkstra SP algorithm
1 parent dee4c65 commit b003f8d

7 files changed

+211
-5
lines changed

README.md

+47
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ $ pip install py-algorithms
5757
- Breadth-First-Search (BFS)
5858
- Topologial sort
5959
- Floyd–Warshall algorithm
60+
- Dijkstra's algorithm
6061

6162
---
6263

@@ -530,6 +531,52 @@ topological_sort_f1(dag) #=> [#<Vertex(C)>, #<Vertex(B)>, #<Vertex(D)>, #<Vertex
530531

531532
```
532533

534+
### Dijkstra's algorithm
535+
536+
![](https://upload.wikimedia.org/wikipedia/commons/5/57/Dijkstra_Animation.gif)
537+
538+
```python
539+
from py_algorithms.graph import new_undirected_graph
540+
from py_algorithms.graph new_dijkstra_shortest_path
541+
from py_algorithms.graph reconstruct_dijkstra_path_tree
542+
543+
graph = new_undirected_graph()
544+
a = graph.insert_vertex('A')
545+
b = graph.insert_vertex('B')
546+
c = graph.insert_vertex('C')
547+
d = graph.insert_vertex('D')
548+
e = graph.insert_vertex('E')
549+
550+
graph.insert_edge(a, e, 10)
551+
graph.insert_edge(b, d, 2)
552+
graph.insert_edge(c, d, 3)
553+
graph.insert_edge(d, e, 12)
554+
555+
# Start from vertex "c" and grow the cluster.
556+
paths = new_dijkstra_shortest_path(graph, c)
557+
558+
assert paths[c] == 0
559+
assert paths[d] == 3
560+
assert paths[b] == 5
561+
assert paths[e] == 15
562+
assert paths[a] == 25
563+
564+
# Reconstructing particulat shortest path
565+
path_tree = reconstruct_dijkstra_path_tree(graph, c, paths)
566+
567+
root = a
568+
stack = []
569+
while root != c:
570+
stack.append(root)
571+
root = path_tree[root].opposite(root)
572+
stack.append(root)
573+
574+
575+
# The shortest path to reach vertex "a":
576+
assert stack == [a, e, d, c]
577+
578+
```
579+
533580
---
534581

535582
### String Algorithms

py_algorithms/graph/__init__.py

+18-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
__all__ = [
22
'Graph',
33
'Vertex',
4-
'directed_graph'
5-
'new_undirected_graph'
6-
'new_topological_sort_f1'
4+
'directed_graph',
5+
'new_undirected_graph',
6+
'new_topological_sort_f1',
7+
'new_dijkstra_shortest_path',
8+
'reconstruct_dijkstra_path_tree',
79
]
810

9-
from typing import List, Callable
11+
from typing import List, Callable, Dict
1012

1113
from .adt.graph import Graph
1214
from .adt.vertex import Vertex
15+
from .adt.edge import Edge
1316
from .topological_sort import TopologicalSort
17+
from .dijkstra_shortest_path import DijkstraShortestPath
18+
from .reconstruct_dijkstra_path_tree import ReconstructDijkstraPathTree
1419

1520

1621
def new_directed_graph() -> Graph:
@@ -25,4 +30,13 @@ def new_topological_sort() -> Callable[[Graph], List[Vertex]]:
2530
return TopologicalSort()
2631

2732

33+
def new_dijkstra_shortest_path(graph, source):
34+
return DijkstraShortestPath.apply(graph, source)
35+
36+
37+
def reconstruct_dijkstra_path_tree(
38+
graph: Graph, source: Vertex, dst: Dict[Vertex, int]) -> Dict[Vertex, Edge]:
39+
return ReconstructDijkstraPathTree.apply(graph, source, dst)
40+
41+
2842
topological_sort_f1 = new_topological_sort()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from typing import Dict
2+
3+
from ..data_structures import new_priority_queue
4+
from .adt.graph import Graph
5+
from .adt.vertex import Vertex
6+
from py_algorithms.data_structures import PriorityQueue
7+
8+
9+
class DijkstraShortestPath:
10+
@classmethod
11+
def apply(cls, graph: Graph, source: Vertex) -> Dict[Vertex, int]:
12+
distances = {}
13+
visited = {}
14+
pq = new_priority_queue(lambda x, y: (x > y) - (x < y) == -1)
15+
16+
for v in graph.vertices():
17+
if v is source:
18+
distances[v] = 0
19+
else:
20+
distances[v] = float('inf')
21+
pq.push(v, distances[v])
22+
23+
while pq.size > 0:
24+
u = pq.pop()
25+
visited[u] = distances[u]
26+
27+
for e in graph.incident_edges(u):
28+
v = e.opposite(u)
29+
30+
if v not in visited:
31+
# perform edge relaxation
32+
w = e.element()
33+
if distances[v] > distances[u] + w:
34+
distances[v] = distances[u] + w
35+
pq = cls._rebuild_pq(pq, distances)
36+
37+
return visited
38+
39+
@staticmethod
40+
def _rebuild_pq(pq: PriorityQueue, dst: Dict[Vertex, int]) -> PriorityQueue:
41+
tmp = []
42+
while pq.size > 0:
43+
tmp.append(pq.pop())
44+
45+
for v in tmp:
46+
pq.push(v, dst[v])
47+
48+
return pq
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from typing import Dict
2+
3+
from .adt.edge import Edge
4+
from .adt.graph import Graph
5+
from .adt.vertex import Vertex
6+
7+
8+
class ReconstructDijkstraPathTree:
9+
@classmethod
10+
def apply(cls, graph: Graph, source: Vertex, dst: Dict[Vertex, int]) -> Dict[Vertex, Edge]:
11+
tree = {}
12+
13+
for v in dst:
14+
if v is not source:
15+
for e in graph.incident_edges(v, False):
16+
u = e.opposite(v)
17+
w = e.element()
18+
if dst[v] == dst[u] + w:
19+
tree[v] = e
20+
return tree

setup.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040
'DAG', 'directed-acyclic-graph', 'simple-graph',
4141
'undirected-graph', 'depth-first-search', 'DFS',
4242
'breadth-first-search', 'BFS', 'Levenshtein Distance',
43-
'levenshtein-distance'],
43+
'levenshtein-distance', 'dijkstras algorithm', 'dijkstras',
44+
'dijkstras shortest path'],
4445
description="Library of Algorithms, Data Structures, "
4546
"variety of solutions to common "
4647
"CS problems.",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from py_algorithms.graph import new_dijkstra_shortest_path
2+
from py_algorithms.graph import new_undirected_graph
3+
from py_algorithms.graph import reconstruct_dijkstra_path_tree
4+
5+
6+
class TestDijkstraShortestPath:
7+
def test_apply(self):
8+
graph = new_undirected_graph()
9+
a = graph.insert_vertex('A')
10+
b = graph.insert_vertex('B')
11+
c = graph.insert_vertex('C')
12+
d = graph.insert_vertex('D')
13+
e = graph.insert_vertex('E')
14+
15+
graph.insert_edge(a, e, 10)
16+
graph.insert_edge(b, d, 2)
17+
graph.insert_edge(c, d, 3)
18+
graph.insert_edge(d, e, 12)
19+
20+
paths = new_dijkstra_shortest_path(graph, c)
21+
22+
assert paths[c] == 0
23+
assert paths[d] == 3
24+
assert paths[b] == 5
25+
assert paths[e] == 15
26+
assert paths[a] == 25
27+
28+
path_tree = reconstruct_dijkstra_path_tree(graph, c, paths)
29+
30+
p = a
31+
stack = []
32+
33+
while p != c:
34+
stack.append(p)
35+
p = path_tree[p].opposite(p)
36+
37+
stack.append(p)
38+
39+
print(stack)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from py_algorithms.graph import new_dijkstra_shortest_path
2+
from py_algorithms.graph import new_undirected_graph
3+
from py_algorithms.graph import reconstruct_dijkstra_path_tree
4+
5+
6+
class TestReconstructDijkstraPathTree:
7+
def test_apply(self):
8+
graph = new_undirected_graph()
9+
a = graph.insert_vertex('A')
10+
b = graph.insert_vertex('B')
11+
c = graph.insert_vertex('C')
12+
d = graph.insert_vertex('D')
13+
e = graph.insert_vertex('E')
14+
15+
graph.insert_edge(a, e, 10)
16+
graph.insert_edge(b, d, 2)
17+
graph.insert_edge(c, d, 3)
18+
graph.insert_edge(d, e, 12)
19+
20+
paths = new_dijkstra_shortest_path(graph, c)
21+
22+
assert paths[c] == 0
23+
assert paths[d] == 3
24+
assert paths[b] == 5
25+
assert paths[e] == 15
26+
assert paths[a] == 25
27+
28+
path_tree = reconstruct_dijkstra_path_tree(graph, c, paths)
29+
30+
p = a
31+
stack = []
32+
while p != c:
33+
stack.append(p)
34+
p = path_tree[p].opposite(p)
35+
stack.append(p)
36+
37+
assert stack == [a, e, d, c]

0 commit comments

Comments
 (0)