Skip to content

Commit 9cef2d4

Browse files
author
Mauricio Klein
committed
Add solution for challenge #32
1 parent a0b5564 commit 9cef2d4

File tree

6 files changed

+135
-2
lines changed

6 files changed

+135
-2
lines changed

Diff for: README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- [x] [Challenge 26: Course Pre-requisites](challenge-26/)
2929
- [x] [Challenge 27: Witness of The Tall People](challenge-27/)
3030
- [x] [Challenge 28: Validate BST](challenge-28/)
31-
-[x] [Challenge 29: Flood fill](challenge-29/)
32-
-[x] [Challenge 30: Merge sort](challenge-30/)
31+
- [x] [Challenge 29: Flood fill](challenge-29/)
32+
- [x] [Challenge 30: Merge sort](challenge-30/)
3333
- [x] [Challenge 31: Find cycle in indirected graph](challenge-31/)
34+
- [x] [Challenge 32: Shortest path in a indirected acyclic graph](challenge-32/)

Diff for: challenge-32/Makefile

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
test:
2+
python test_*.py
3+
4+
.PHONY: test

Diff for: challenge-32/README.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Shortest path in a indirected acyclic graph
2+
3+
## Description
4+
5+
Given a weighted graph, a source node and a target node, find the shortest distance between these two nodes
6+
7+
## Example
8+
9+
```
10+
Input:
11+
a, b, c, d, e = Node('a'), Node('b'), Node('c'), Node('d'), Node('e')
12+
13+
source, target = a, e
14+
15+
# First path (distance: 30)
16+
a.add_edge(b, 10)
17+
b.add_edge(c, 10)
18+
c.add_edge(e, 10)
19+
20+
# Second path (distance: 35)
21+
a.add_edge(d, 25)
22+
d.add_edge(e, 10)
23+
24+
Graph([a,b,c,d,e]).shortest_path(a,e) # [a, b, c, e]
25+
```

Diff for: challenge-32/__init__.py

Whitespace-only changes.

Diff for: challenge-32/solver.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
class Node(object):
2+
def __init__(self, V):
3+
self.V = V
4+
self.edges = {}
5+
6+
def add_edge(self, node, cost):
7+
self.edges[node] = cost
8+
9+
class Graph(object):
10+
def __init__(self, vertices):
11+
self.vertices = {
12+
n: {
13+
'distance': float('inf'),
14+
'previous': None,
15+
'visited' : False
16+
} for n in vertices
17+
}
18+
19+
def min_distance(self):
20+
min_dist = float('inf')
21+
node = None
22+
23+
for v in self.vertices:
24+
if not self.is_visited(v) and self.get_distance(v) < min_dist:
25+
min_dist = self.get_distance(v)
26+
node = v
27+
28+
return node
29+
30+
def is_visited(self, node):
31+
return self.vertices[node]['visited']
32+
33+
def mark_as_visited(self, node):
34+
self.vertices[node]['visited'] = True
35+
36+
def get_distance(self, node):
37+
return self.vertices[node]['distance']
38+
39+
def set_distance(self, node, distance):
40+
self.vertices[node]['distance'] = distance
41+
42+
def set_previous(self, node, previous):
43+
self.vertices[node]['previous'] = previous
44+
45+
def get_previous_from(self, node):
46+
return self.vertices[node]['previous']
47+
48+
def dijkstra(self, source):
49+
# Initialize source with zero distance
50+
self.set_distance(source, 0)
51+
52+
node = self.min_distance()
53+
54+
while node:
55+
for edge, edge_distance in node.edges.items():
56+
if self.is_visited(edge):
57+
continue
58+
59+
# Calculate the distance to the edge and, if shorter,
60+
# updates the vertex distance and previous node
61+
d = self.get_distance(node) + edge_distance
62+
if d < self.get_distance(edge):
63+
self.set_distance(edge, d)
64+
self.set_previous(edge, node)
65+
66+
self.mark_as_visited(node)
67+
node = self.min_distance()
68+
69+
def shortest_path(self, source, target):
70+
self.dijkstra(source)
71+
72+
path, node = [], target
73+
while node:
74+
path.append(node)
75+
node = self.get_previous_from(node)
76+
77+
path.reverse()
78+
79+
return path

Diff for: challenge-32/test_solver.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import unittest
2+
from solver import Graph, Node
3+
4+
class TestSolver(unittest.TestCase):
5+
def test_connected_graph(self):
6+
a, b, c, d, e = Node('a'), Node('b'), Node('c'), Node('d'), Node('e')
7+
8+
source, target = a, e
9+
10+
# First path (distance: 30)
11+
a.add_edge(b, 10); b.add_edge(a, 10);
12+
b.add_edge(c, 10); c.add_edge(b, 10);
13+
c.add_edge(e, 10); e.add_edge(c, 10);
14+
15+
# Second path (distance: 35)
16+
a.add_edge(d, 25); d.add_edge(a, 25);
17+
d.add_edge(e, 10); e.add_edge(d, 10);
18+
19+
g = Graph([a,b,c,d,e])
20+
21+
self.assertEqual(g.shortest_path(a, e), [a, b, c, e])
22+
23+
if __name__ == "__main__":
24+
unittest.main()

0 commit comments

Comments
 (0)