Skip to content

Commit bd1953a

Browse files
authored
Add Dijkstra's algorithm as a problem (#115)
1 parent f5262c9 commit bd1953a

7 files changed

+181
-54
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ Welcome to **Data Structures and Algorithms in Go**! 🎉 This project is design
114114
* [Employee Headcount](./graph/employee_headcount_test.go)
115115
* [Remove Invalid Parentheses](./graph/remove_invalid_parentheses_test.go)
116116
* [Cheapest Flights](./graph/cheapest_flights_test.go)
117+
* [Dijkstra's Algorithm](./graph/dijkstra_test.go)
117118
* [Word Ladder](./graph/word_ladder_test.go)
118119
* [Network Delay Time](./graph/network_delay_time_test.go)
119120
* [Number of Islands](./graph/number_of_islands_test.go)

graph/README.md

+4-54
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ A graph is a collection of vertices connected through directed or undirected edg
55
```ASCII
66
[Figure 1] Graph Examples - Numbers, arrows, and numbers in brackets represent vertices, edges, and edge weights.
77
8-
5 3 5 3 6 1 - Is it a DAG?
8+
5 3 5 3 5 1 - Is it a DAG?
99
↗ ↑ ↖ ↗ ↘ ↖ ↘ (4) ↖ (1) 2- Is it a connected graph?
1010
2 → 3 ← 4 2 4 4 ← 2 2 ─────→ 4 A 1 1
1111
↖ ↑ ↗ ↖ ↙ ↗ (1) ↑ (2) ↗ (1) B 0 1
12-
1 1 1 1 ──→ 5 C 1 0
12+
1 1 1 1 ──→ 3 C 1 0
1313
D 1 1
1414
(A) (B) (C) (D)
1515
```
@@ -255,58 +255,7 @@ Discovery of a back edge in a DFS algorithm indicates a cyclic graph.
255255

256256
### Dijkstra's Algorithm
257257

258-
A [greedy](../greedy) algorithm that uses BFS-like ideas and a minimum [heap](../heap) to solve single-source shortest path problems in edge-weighted directed graphs like (D) in _Figure 1_.
259-
260-
```Go
261-
package graph_test
262-
263-
import "container/heap"
264-
265-
type (
266-
dijkstraVertex struct {
267-
val int
268-
distance int
269-
discovered bool
270-
edges map[]*dijkstraVertex
271-
}
272-
verticesHeap []*dijkstraVertex
273-
)
274-
275-
func dijkstra(graph []*timedVertex) {
276-
h := new(pointsHeap)
277-
for _, vertex := range graph {
278-
heap.Push(h, vertex)
279-
}
280-
281-
s := make(map[*Vertex]struct{})
282-
for h.Len() != 0 {
283-
u := h.Pop().(*Vertex)
284-
u.discovered = true
285-
286-
for _, v := range u.Edges {
287-
if v.discovered {
288-
continue
289-
}
290-
291-
if v.distance + cvw < w.distance {
292-
w.distance
293-
}
294-
}
295-
}
296-
}
297-
298-
func (p verticesHeap) Len() int { return len(p) }
299-
func (p verticesHeap) Less(i, j int) bool { return p[i].distance < p[j].distance }
300-
func (p verticesHeap) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
301-
func (p *verticesHeap) Push(x interface{}) { *p = append(*p, x.(*Vertex)) }
302-
303-
func (p *pointsHeap) Pop() interface{} {
304-
old := *p
305-
tmp := old[len(old)-1]
306-
*p = old[0 : len(old)-1]
307-
return tmp
308-
}
309-
```
258+
A [greedy](../greedy) algorithm that uses BFS-like ideas and a minimum [heap](../heap) to solve single-source shortest path problems in edge-weighted directed graphs like finding the shortest path between 1 and 5 in graph (D) in _Figure 1_. The [Dijkstra's Algorithm](./dijkstra_test.go) rehearsal shows an implementation of it.
310259

311260
## Complexity
312261

@@ -326,6 +275,7 @@ Graph algorithms find extensive usage in addressing real-world challenges, inclu
326275
* [Employee Headcount](./employee_headcount_test.go), [Solution](./employee_headcount.go)
327276
* [Remove Invalid Parentheses](./remove_invalid_parentheses_test.go), [Solution](./remove_invalid_parentheses.go)
328277
* [Cheapest Flights](./cheapest_flights_test.go), [Solution](./cheapest_flights.go)
278+
* [Dijkstra's Algorithm](./dijkstra_test.go), [Solution](./dijkstra.go)
329279
* [Word Ladder](./word_ladder_test.go), [Solution](./word_ladder.go)
330280
* [Network Delay Time](./network_delay_time_test.go), [Solution](./network_delay_time.go)
331281
* [Number of Islands](./number_of_islands_test.go), [Solution](./number_of_islands.go)

graph/dijkstra.go

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package graph
2+
3+
import (
4+
"container/heap"
5+
"math"
6+
)
7+
8+
type (
9+
dijkstraVertex struct {
10+
val int
11+
distance int
12+
discovered bool
13+
edges []*weightedEdge
14+
}
15+
weightedEdge struct {
16+
edge *dijkstraVertex
17+
weight int
18+
}
19+
verticesHeap []*dijkstraVertex
20+
)
21+
22+
// Dijkstra solves the problem in O(ElogV) time and O(V) space.
23+
func Dijkstra(graph []*dijkstraVertex, source *dijkstraVertex) {
24+
for _, vertex := range graph {
25+
vertex.distance = math.MaxInt
26+
vertex.discovered = false
27+
}
28+
29+
source.distance = 0
30+
31+
minHeap := new(verticesHeap)
32+
heap.Push(minHeap, source)
33+
34+
for minHeap.Len() != 0 {
35+
u := heap.Pop(minHeap).(*dijkstraVertex)
36+
u.discovered = true
37+
38+
for _, edge := range u.edges {
39+
v := edge.edge
40+
if v.discovered {
41+
continue
42+
}
43+
44+
if u.distance+edge.weight < v.distance {
45+
v.distance = u.distance + edge.weight
46+
heap.Push(minHeap, v)
47+
}
48+
}
49+
}
50+
}
51+
52+
func (p verticesHeap) Len() int { return len(p) }
53+
func (p verticesHeap) Less(i, j int) bool { return p[i].distance < p[j].distance }
54+
func (p verticesHeap) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
55+
func (p *verticesHeap) Push(x interface{}) { *p = append(*p, x.(*dijkstraVertex)) }
56+
func (p *verticesHeap) Pop() interface{} {
57+
old := *p
58+
tmp := old[len(old)-1]
59+
*p = old[0 : len(old)-1]
60+
return tmp
61+
}

graph/dijkstra_test.go

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package graph
2+
3+
import (
4+
"testing"
5+
)
6+
7+
type dijkstraTestCase struct {
8+
graph []*dijkstraVertex
9+
distances []int
10+
}
11+
12+
/*
13+
TestDijkstra tests solution(s) with the following signature and problem description:
14+
15+
dijkstraVertex struct {
16+
val int
17+
distance int
18+
discovered bool
19+
edges []*weightedEdge
20+
}
21+
weightedEdge struct {
22+
edge *dijkstraVertex
23+
weight int
24+
}
25+
func Dijkstra(graph []*dijkstraVertex, source *dijkstraVertex)
26+
27+
Implement Dijkstra's algorithm. Given a single sourced edge weighted graph, find the
28+
shortest path from the source of the graph to every node and set it to the distance
29+
field of the node.
30+
31+
5
32+
(4) ↖ (1)
33+
2 ─────→ 4
34+
(1) ↑ (2) ↗ (1)
35+
1 ──→ 3
36+
37+
For the above graph, the shortest paths for each node are {0, 1, 3, 5, 8}.
38+
*/
39+
func TestDijkstra(t *testing.T) {
40+
tests := []*dijkstraTestCase{
41+
{
42+
graph: newGraph(2),
43+
distances: []int{0, 10},
44+
},
45+
{
46+
graph: newGraph(5),
47+
distances: []int{0, 1, 3, 5, 8},
48+
},
49+
{
50+
graph: newGraph(4),
51+
distances: []int{0, 5, 3, 10},
52+
},
53+
{
54+
graph: newGraph(3),
55+
distances: []int{0, 3, 5},
56+
},
57+
{
58+
graph: newGraph(6),
59+
distances: []int{0, 1, 3, 4, 6, 10},
60+
},
61+
}
62+
addEdges(tests)
63+
64+
for i, test := range tests {
65+
Dijkstra(test.graph, test.graph[0])
66+
for j, v := range test.graph {
67+
if v.distance != test.distances[j] {
68+
t.Errorf("Test Case %d, Vertex %d distance: got %d, expected %d", i, v.val, v.distance, test.distances[j])
69+
}
70+
}
71+
}
72+
}
73+
74+
func newGraph(edges int) []*dijkstraVertex {
75+
graph := make([]*dijkstraVertex, edges)
76+
for i := 0; i < edges; i++ {
77+
graph[i] = &dijkstraVertex{val: i}
78+
}
79+
return graph
80+
}
81+
82+
func addEdges(tests []*dijkstraTestCase) {
83+
tests[0].graph[0].edges = []*weightedEdge{{edge: tests[0].graph[1], weight: 10}}
84+
85+
tests[1].graph[0].edges = []*weightedEdge{{edge: tests[1].graph[1], weight: 1}, {edge: tests[1].graph[2], weight: 3}}
86+
tests[1].graph[1].edges = []*weightedEdge{{edge: tests[1].graph[2], weight: 2}, {edge: tests[1].graph[3], weight: 4}}
87+
tests[1].graph[2].edges = []*weightedEdge{{edge: tests[1].graph[3], weight: 3}, {edge: tests[1].graph[4], weight: 5}}
88+
tests[1].graph[3].edges = []*weightedEdge{{edge: tests[1].graph[4], weight: 5}}
89+
90+
tests[2].graph[0].edges = []*weightedEdge{{edge: tests[2].graph[1], weight: 5}, {edge: tests[2].graph[2], weight: 3}}
91+
tests[2].graph[1].edges = []*weightedEdge{{edge: tests[2].graph[2], weight: 2}, {edge: tests[2].graph[3], weight: 6}}
92+
tests[2].graph[2].edges = []*weightedEdge{{edge: tests[2].graph[3], weight: 7}}
93+
94+
tests[3].graph[0].edges = []*weightedEdge{{edge: tests[3].graph[1], weight: 3}, {edge: tests[3].graph[2], weight: 5}}
95+
tests[3].graph[1].edges = []*weightedEdge{{edge: tests[3].graph[2], weight: 2}}
96+
97+
tests[4].graph[0].edges = []*weightedEdge{{edge: tests[4].graph[1], weight: 1}, {edge: tests[4].graph[2], weight: 4}}
98+
tests[4].graph[1].edges = []*weightedEdge{{edge: tests[4].graph[2], weight: 2}, {edge: tests[4].graph[3], weight: 5}}
99+
tests[4].graph[2].edges = []*weightedEdge{{edge: tests[4].graph[3], weight: 1}, {edge: tests[4].graph[4], weight: 3}}
100+
tests[4].graph[3].edges = []*weightedEdge{{edge: tests[4].graph[4], weight: 2}, {edge: tests[4].graph[1], weight: 1}}
101+
tests[4].graph[4].edges = []*weightedEdge{{edge: tests[4].graph[5], weight: 4}}
102+
}

graph/is_dag_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import "testing"
55
/*
66
TestIsDAG tests solution(s) with the following signature and problem description:
77
8+
Vertex struct {
9+
Val int
10+
Edges []*Vertex
11+
}
812
func IsDAG(graph []*Vertex) bool
913
1014
Given a directed graph determine if it's a DAG or not. A directed acyclic graph (DAG)

graph/iterative_traversal_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import (
88
/*
99
TestIterativeTraversal tests solution(s) with the following signature and problem description:
1010
11+
Vertex struct {
12+
Val int
13+
Edges []*Vertex
14+
}
1115
func IterativeTraversal(graph []*Vertex) ([]int, []int)
1216
1317
Implement BFS and DFS on a graph without using recursion, and return the value of each vertex

graph/topological_sort_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ var readmeGraphs = map[string][][]int{
1515
/*
1616
TestTopologicalSort tests solution(s) with the following signature and problem description:
1717
18+
type VertexWithIngress struct {
19+
Val any
20+
Edges []*VertexWithIngress
21+
Ingress int
22+
}
1823
func TopologicalSort(graph []*VertexWithIngress) ([]int, error)
1924
2025
In a DAG the topological order returns elements such that if there's a path from v(i) to

0 commit comments

Comments
 (0)