Skip to content

Commit 4768b66

Browse files
committed
Merge branch 'feature/graph-algorithms' into develop
resolve conflicts
2 parents 49496e8 + 5c000e7 commit 4768b66

File tree

6 files changed

+199
-51
lines changed

6 files changed

+199
-51
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ algorithm DFS-VISIT(G, u):
260260
u.finished = time
261261
```
262262
263-
- Dijkstra's algorithm: [c++](cpp-algorithm/src/graph), [java](java-algorithm/src/main/java/com/example/algorithm/graph) | A single source the shortest path algorithm that handle non-negative edge weights. It find the shortest path between two vertices in a graph.
263+
- Dijkstra's algorithm, CCSP#4.5.1: [c++](cpp-algorithm/src/graph),[python](python-algorithm/algorithm/graph/test)(test), [java](java-algorithm/src/main/java/com/example/algorithm/graph) | A single source the shortest path algorithm that handle non-negative edge weights. It find the shortest path between two vertices in a graph.
264264

265265
```txt
266266
algorithm Dijkstra(G, source):
@@ -347,6 +347,7 @@ algorithm Prim(G, root):
347347
**Examples**
348348

349349
- Maze problem: [java](java-algorithm/src/main/java/com/example/algorithm/graph) | A maze problem is that find a path from the start to the goal. The maze is represented by a graph. The start and the goal are represented by vertices. The path is represented by a sequence of vertices.
350+
- Minimum spanning tree (Kruskal, Prim, Boruvka), CCSP#4.4.2: [python](python-algorithm/algorithm/graph/test)(test) | Find the minimum spanning tree of a graph.
350351

351352
[:arrow_up_small: back to toc](#table-of-contents)
352353

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import networkx
2+
3+
seattle = 'Seattle'
4+
san_francisco = 'San Francisco'
5+
los_angeles = 'Los Angeles'
6+
riverside = 'Riverside'
7+
phoenix = 'Phoenix'
8+
dallas = 'Dallas'
9+
houston = 'Houston'
10+
atlanta = 'Atlanta'
11+
miami = 'Miami'
12+
chicago = 'Chicago'
13+
detroit = 'Detroit'
14+
boston = 'Boston'
15+
new_york = 'New York'
16+
philadelphia = 'Philadelphia'
17+
washington = 'Washington'
18+
19+
20+
def create_city_graph() -> networkx.Graph:
21+
edges = [
22+
(seattle, chicago),
23+
(seattle, san_francisco),
24+
(san_francisco, riverside),
25+
(san_francisco, los_angeles),
26+
(los_angeles, riverside),
27+
(los_angeles, phoenix),
28+
(riverside, phoenix),
29+
(riverside, chicago),
30+
(phoenix, dallas),
31+
(phoenix, houston),
32+
(dallas, chicago),
33+
(dallas, atlanta),
34+
(dallas, houston),
35+
(houston, atlanta),
36+
(houston, miami),
37+
(atlanta, chicago),
38+
(atlanta, washington),
39+
(atlanta, miami),
40+
(miami, washington),
41+
(chicago, detroit),
42+
(detroit, boston),
43+
(detroit, washington),
44+
(detroit, new_york),
45+
(boston, new_york),
46+
(new_york, philadelphia),
47+
(philadelphia, washington)
48+
]
49+
graph = networkx.Graph()
50+
graph.add_edges_from(edges)
51+
return graph
52+
53+
54+
def create_weighted_city_graph() -> networkx.Graph:
55+
weighted_edges = [
56+
(seattle, chicago, 1737),
57+
(seattle, san_francisco, 678),
58+
(san_francisco, riverside, 386),
59+
(san_francisco, los_angeles, 348),
60+
(los_angeles, riverside, 50),
61+
(los_angeles, phoenix, 357),
62+
(riverside, phoenix, 307),
63+
(riverside, chicago, 1704),
64+
(phoenix, dallas, 887),
65+
(phoenix, houston, 1015),
66+
(dallas, chicago, 805),
67+
(dallas, atlanta, 721),
68+
(dallas, houston, 225),
69+
(houston, atlanta, 702),
70+
(houston, miami, 968),
71+
(atlanta, chicago, 588),
72+
(atlanta, washington, 543),
73+
(atlanta, miami, 604),
74+
(miami, washington, 923),
75+
(chicago, detroit, 238),
76+
(detroit, boston, 613),
77+
(detroit, washington, 396),
78+
(detroit, new_york, 482),
79+
(boston, new_york, 190),
80+
(new_york, philadelphia, 81),
81+
(philadelphia, washington, 123)
82+
]
83+
graph = networkx.Graph()
84+
graph.add_weighted_edges_from(weighted_edges)
85+
return graph

python-algorithm/algorithm/graph/test/test_breadth_first_search.py

Lines changed: 21 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,7 @@
1-
import pytest, networkx
2-
3-
4-
def create_city_graph() -> networkx.Graph:
5-
graph = networkx.Graph()
6-
graph.add_edge("Seattle", "Chicago")
7-
graph.add_edge("Seattle", "San Francisco")
8-
graph.add_edge("San Francisco", "Riverside")
9-
graph.add_edge("San Francisco", "Los Angeles")
10-
graph.add_edge("Los Angeles", "Riverside")
11-
graph.add_edge("Los Angeles", "Phoenix")
12-
graph.add_edge("Riverside", "Phoenix")
13-
graph.add_edge("Riverside", "Chicago")
14-
graph.add_edge("Phoenix", "Dallas")
15-
graph.add_edge("Phoenix", "Houston")
16-
graph.add_edge("Dallas", "Chicago")
17-
graph.add_edge("Dallas", "Atlanta")
18-
graph.add_edge("Dallas", "Houston")
19-
graph.add_edge("Houston", "Atlanta")
20-
graph.add_edge("Houston", "Miami")
21-
graph.add_edge("Atlanta", "Chicago")
22-
graph.add_edge("Atlanta", "Washington")
23-
graph.add_edge("Atlanta", "Miami")
24-
graph.add_edge("Miami", "Washington")
25-
graph.add_edge("Chicago", "Detroit")
26-
graph.add_edge("Detroit", "Boston")
27-
graph.add_edge("Detroit", "Washington")
28-
graph.add_edge("Detroit", "New York")
29-
graph.add_edge("Boston", "New York")
30-
graph.add_edge("New York", "Philadelphia")
31-
graph.add_edge("Philadelphia", "Washington")
32-
return graph
1+
import networkx
2+
import pytest
333

4+
from algorithm.graph.test.graph_data_utils import create_city_graph
345

356
city_graph = create_city_graph()
367

@@ -39,21 +10,21 @@ def create_city_graph() -> networkx.Graph:
3910
@pytest.mark.parametrize(
4011
argnames='graph, source, expected_neighbors',
4112
argvalues=[
42-
(city_graph, "Seattle", ["Chicago", "San Francisco"]),
43-
(city_graph, "San Francisco", ["Seattle", "Riverside", "Los Angeles"]),
44-
(city_graph, "Los Angeles", ["San Francisco", "Riverside", "Phoenix"]),
45-
(city_graph, "Riverside", ["San Francisco", "Los Angeles", "Phoenix", "Chicago"]),
46-
(city_graph, "Phoenix", ["Los Angeles", "Riverside", "Dallas", "Houston"]),
47-
(city_graph, "Chicago", ["Seattle", "Riverside", "Dallas", "Atlanta", "Detroit"]),
48-
(city_graph, "Boston", ["Detroit", "New York"]),
49-
(city_graph, "New York", ["Detroit", "Boston", "Philadelphia"]),
50-
(city_graph, "Atlanta", ["Dallas", "Houston", "Chicago", "Washington", "Miami"]),
51-
(city_graph, "Miami", ["Houston", "Atlanta", "Washington"]),
52-
(city_graph, "Dallas", ["Phoenix", "Chicago", "Atlanta", "Houston"]),
53-
(city_graph, "Houston", ["Phoenix", "Dallas", "Atlanta", "Miami"]),
54-
(city_graph, "Detroit", ["Chicago", "Boston", "Washington", "New York"]),
55-
(city_graph, "Philadelphia", ["New York", "Washington"]),
56-
(city_graph, "Washington", ["Atlanta", "Miami", "Detroit", "Philadelphia"])
13+
(city_graph, 'Seattle', ['Chicago', 'San Francisco']),
14+
(city_graph, 'San Francisco', ['Seattle', 'Riverside', 'Los Angeles']),
15+
(city_graph, 'Los Angeles', ['San Francisco', 'Riverside', 'Phoenix']),
16+
(city_graph, 'Riverside', ['San Francisco', 'Los Angeles', 'Phoenix', 'Chicago']),
17+
(city_graph, 'Phoenix', ['Los Angeles', 'Riverside', 'Dallas', 'Houston']),
18+
(city_graph, 'Chicago', ['Seattle', 'Riverside', 'Dallas', 'Atlanta', 'Detroit']),
19+
(city_graph, 'Boston', ['Detroit', 'New York']),
20+
(city_graph, 'New York', ['Detroit', 'Boston', 'Philadelphia']),
21+
(city_graph, 'Atlanta', ['Dallas', 'Houston', 'Chicago', 'Washington', 'Miami']),
22+
(city_graph, 'Miami', ['Houston', 'Atlanta', 'Washington']),
23+
(city_graph, 'Dallas', ['Phoenix', 'Chicago', 'Atlanta', 'Houston']),
24+
(city_graph, 'Houston', ['Phoenix', 'Dallas', 'Atlanta', 'Miami']),
25+
(city_graph, 'Detroit', ['Chicago', 'Boston', 'Washington', 'New York']),
26+
(city_graph, 'Philadelphia', ['New York', 'Washington']),
27+
(city_graph, 'Washington', ['Atlanta', 'Miami', 'Detroit', 'Philadelphia'])
5728
])
5829
def test_graph_neighbors(benchmark, graph, source, expected_neighbors):
5930
"""
@@ -71,9 +42,9 @@ def test_graph_neighbors(benchmark, graph, source, expected_neighbors):
7142
@pytest.mark.parametrize(
7243
argnames='graph, source, distance, expected_nodes',
7344
argvalues=[
74-
(city_graph, "Boston", 1, ["Detroit", "New York"]),
75-
(city_graph, "Boston", 2, ["Chicago", "Washington", "Philadelphia"]),
76-
(city_graph, "Boston", 3, ["Seattle", "Riverside", "Dallas", "Atlanta", "Miami"]),
45+
(city_graph, 'Boston', 1, ['Detroit', 'New York']),
46+
(city_graph, 'Boston', 2, ['Chicago', 'Washington', 'Philadelphia']),
47+
(city_graph, 'Boston', 3, ['Seattle', 'Riverside', 'Dallas', 'Atlanta', 'Miami']),
7748
],
7849
ids=['distance1', 'distance2', 'distance3'])
7950
def test_graph_breadth_first_search(benchmark, graph, source, distance, expected_nodes):
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import networkx
2+
import pytest
3+
4+
from algorithm.graph.test.graph_data_utils import create_weighted_city_graph
5+
6+
city_graph = create_weighted_city_graph()
7+
8+
9+
@pytest.mark.benchmark(group='graph_dijkstra')
10+
@pytest.mark.parametrize(
11+
argnames='graph, source, target, expected_path',
12+
argvalues=[
13+
(city_graph, 'Los Angeles', 'Boston', ['Los Angeles', 'Riverside', 'Chicago', 'Detroit', 'Boston']),
14+
],
15+
ids=['los_angeles_to_boston'])
16+
def test_graph_dijkstra_path(benchmark, graph, source, target, expected_path):
17+
"""
18+
Find the shortest path between two nodes in a graph using Dijkstra's algorithm.
19+
:param benchmark: benchmark fixture
20+
:param graph: city graph of the United States
21+
:param source: source city node
22+
:param target: target city node
23+
:param expected_path: expected shortest path
24+
"""
25+
shortest_path = benchmark(networkx.dijkstra_path, graph, source, target)
26+
assert shortest_path == expected_path
27+
28+
29+
@pytest.mark.benchmark(group='graph_dijkstra')
30+
@pytest.mark.parametrize(
31+
argnames='graph, source, target, expected_distance',
32+
argvalues=[
33+
(city_graph, 'Los Angeles', 'Boston', 2605),
34+
],
35+
ids=['los_angeles_to_boston'])
36+
def test_graph_dijkstra_distance(benchmark, graph, source, target, expected_distance):
37+
"""
38+
Find the shortest distance between two nodes in a graph using Dijkstra's algorithm.
39+
:param benchmark: benchmark fixture
40+
:param graph: city graph of the United States
41+
:param source: source city node
42+
:param target: target city node
43+
:param expected_distance: expected shortest distance (weight)
44+
"""
45+
total_weight = benchmark(networkx.dijkstra_path_length, graph, source, target)
46+
assert total_weight == expected_distance
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import matplotlib.pyplot as plt
2+
import networkx
3+
import pytest
4+
5+
from algorithm.graph.test.graph_data_utils import create_weighted_city_graph
6+
7+
city_graph = create_weighted_city_graph()
8+
9+
10+
@pytest.mark.skip(reason='This test is for visualization only')
11+
def test_graph_mst_temp():
12+
mst = networkx.minimum_spanning_tree(city_graph, algorithm='kruskal')
13+
pos = networkx.spring_layout(mst)
14+
networkx.draw_networkx_nodes(mst, pos)
15+
networkx.draw_networkx_edges(mst, pos, width=1)
16+
networkx.draw_networkx_labels(mst, pos, font_size=10)
17+
plt.show()
18+
19+
20+
city_edges = {('Seattle', 'San Francisco'), ('San Francisco', 'Los Angeles'), ('Los Angeles', 'Riverside'),
21+
('Riverside', 'Phoenix'), ('Phoenix', 'Dallas'), ('Dallas', 'Houston'), ('Houston', 'Atlanta'),
22+
('Atlanta', 'Miami'), ('Atlanta', 'Washington'), ('Washington', 'Philadelphia'),
23+
('Philadelphia', 'New York'), ('New York', 'Boston'), ('Washington', 'Detroit'), ('Detroit', 'Chicago')}
24+
25+
26+
@pytest.mark.benchmark(group='graph_minimum_spanning_tree')
27+
@pytest.mark.parametrize(
28+
argnames='graph, algorithm, expected_total_weight, expected_edges',
29+
argvalues=[
30+
(city_graph, 'kruskal', 5372, city_edges),
31+
(city_graph, 'prim', 5372, city_edges),
32+
(city_graph, 'boruvka', 5372, city_edges),
33+
],
34+
ids=['kruskal', 'prim', 'boruvka'])
35+
def test_graph_mst(benchmark, graph, algorithm, expected_total_weight, expected_edges):
36+
mst = benchmark(networkx.minimum_spanning_tree, graph, algorithm=algorithm)
37+
38+
mst_total_weight = sum(d['weight'] for u, v, d in mst.edges(data=True))
39+
assert expected_total_weight == mst_total_weight
40+
41+
mst_edges = set((u, v) for u, v, d in mst.edges(data=True))
42+
normalized_set1 = {tuple(sorted(edge)) for edge in expected_edges}
43+
normalized_set2 = {tuple(sorted(edge)) for edge in mst_edges}
44+
assert normalized_set1 == normalized_set2

python-algorithm/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ python = ">=3.11,<3.13"
1111
numpy = "^1.26.0"
1212
scipy = "^1.12.0"
1313
networkx = "^3.2.1"
14+
matplotlib = "^3.8.2"
1415

1516
[tool.poetry.group.test.dependencies]
1617
pytest = "^8.0.0"

0 commit comments

Comments
 (0)