forked from deutranium/Algorithms
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdijkstra.go
213 lines (175 loc) · 5.1 KB
/
dijkstra.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
package main
import (
"fmt"
"sort"
"strconv"
)
type Graph struct {
Edges []*Edge
Nodes []*Node
}
type Edge struct {
Parent *Node
Child *Node
Cost int
}
type Node struct {
Name string
}
const Infinity = int(^uint(0) >> 1)
// AddEdge adds an Edge to the Graph
func (g *Graph) AddEdge(parent, child *Node, cost int) {
edge := &Edge{
Parent: parent,
Child: child,
Cost: cost,
}
g.Edges = append(g.Edges, edge)
g.AddNode(parent)
g.AddNode(child)
}
// AddNode adds a Node to the Graph list of Nodes, if the the node wasn't already added
func (g *Graph) AddNode(node *Node) {
var isPresent bool
for _, n := range g.Nodes {
if n == node {
isPresent = true
}
}
if !isPresent {
g.Nodes = append(g.Nodes, node)
}
}
// String returns a string representation of the Graph
func (g *Graph) String() string {
var s string
s += "Edges:\n"
for _, edge := range g.Edges {
s += edge.Parent.Name + " -> " + edge.Child.Name + " = " + strconv.Itoa(edge.Cost)
s += "\n"
}
s += "\n"
s += "Nodes: "
for i, node := range g.Nodes {
if i == len(g.Nodes)-1 {
s += node.Name
} else {
s += node.Name + ", "
}
}
s += "\n"
return s
}
// Dijkstra implements THE Dijkstra algorithm
// Returns the shortest path from startNode to all the other Nodes
func (g *Graph) Dijkstra(startNode *Node) (shortestPathTable string) {
// First, we instantiate a "Cost Table", it will hold the information:
// "From startNode, what's is the cost to all the other Nodes?"
// When initialized, It looks like this:
// NODE COST
// A 0 // The startNode has always the lowest cost to itself, in this case, 0
// B Inf // the distance to all the other Nodes are unknown, so we mark as Infinity
// C Inf
// ...
costTable := g.NewCostTable(startNode)
// An empty list of "visited" Nodes. Everytime the algorithm runs on a Node, we add it here
var visited []*Node
// A loop to visit all Nodes
for len(visited) != len(g.Nodes) {
// Get closest non visited Node (lower cost) from the costTable
node := getClosestNonVisitedNode(costTable, visited)
// Mark Node as visited
visited = append(visited, node)
// Get Node's Edges (its neighbors)
nodeEdges := g.GetNodeEdges(node)
for _, edge := range nodeEdges {
// The distance to that neighbor, let's say B is the cost from the costTable + the cost to get there (Edge cost)
// In the first run, the costTable says it's "Infinity"
// Plus the actual cost, let's say "5"
// The distance becomes "5"
distanceToNeighbor := costTable[node] + edge.Cost
// If the distance above is lesser than the distance currently in the costTable for that neighbor
if distanceToNeighbor < costTable[edge.Child] {
// Update the costTable for that neighbor
costTable[edge.Child] = distanceToNeighbor
}
}
}
// Make the costTable nice to read :)
for node, cost := range costTable {
shortestPathTable += fmt.Sprintf("Distance from %s to %s = %d\n", startNode.Name, node.Name, cost)
}
return shortestPathTable
}
// NewCostTable returns an initialized cost table for the Dijkstra algorithm work with
// by default, the lowest cost is assigned to the startNode – so the algorithm starts from there
// all the other Nodes in the Graph receives the Infinity value
func (g *Graph) NewCostTable(startNode *Node) map[*Node]int {
costTable := make(map[*Node]int)
costTable[startNode] = 0
for _, node := range g.Nodes {
if node != startNode {
costTable[node] = Infinity
}
}
return costTable
}
// GetNodeEdges returns all the Edges that start with the specified Node
// In other terms, returns all the Edges connecting to the Node's neighbors
func (g *Graph) GetNodeEdges(node *Node) (edges []*Edge) {
for _, edge := range g.Edges {
if edge.Parent == node {
edges = append(edges, edge)
}
}
return edges
}
// getClosestNonVisitedNode returns the closest Node (with the lower cost) from the costTable
// **if the node hasn't been visited yet**
func getClosestNonVisitedNode(costTable map[*Node]int, visited []*Node) *Node {
type CostTableToSort struct {
Node *Node
Cost int
}
var sorted []CostTableToSort
// Verify if the Node has been visited already
for node, cost := range costTable {
var isVisited bool
for _, visitedNode := range visited {
if node == visitedNode {
isVisited = true
}
}
// If not, add them to the sorted slice
if !isVisited {
sorted = append(sorted, CostTableToSort{node, cost})
}
}
// We need the Node with the lower cost from the costTable
// So it's important to sort it
// Here I'm using an anonymous struct to make it easier to sort a map
sort.Slice(sorted, func(i, j int) bool {
return sorted[i].Cost < sorted[j].Cost
})
return sorted[0].Node
}
func main() {
a := &Node{Name: "a"}
b := &Node{Name: "b"}
c := &Node{Name: "c"}
d := &Node{Name: "d"}
e := &Node{Name: "e"}
f := &Node{Name: "f"}
g := &Node{Name: "g"}
graph := Graph{}
graph.AddEdge(a, c, 2)
graph.AddEdge(a, b, 5)
graph.AddEdge(c, b, 1)
graph.AddEdge(c, d, 9)
graph.AddEdge(b, d, 4)
graph.AddEdge(d, e, 2)
graph.AddEdge(d, g, 30)
graph.AddEdge(d, f, 10)
graph.AddEdge(f, g, 1)
fmt.Println(graph.Dijkstra(a))
}