-
Notifications
You must be signed in to change notification settings - Fork 0
/
dijkstra.go
182 lines (155 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
package main
import (
"container/heap"
"fmt"
"math"
"sort"
"strings"
)
// PriorityQueue implements a priority queue for Dijkstra's algorithm.
type PriorityQueue []*Item
// Item represents a node and its priority in the priority queue.
type Item struct {
Node string // Node label.
Priority int // Priority of the node, used for shortest path computation.
Index int // Index of the item in the priority queue.
}
// Len returns the length of the priority queue.
func (pq PriorityQueue) Len() int { return len(pq) }
// Less compares two items in the priority queue based on their priority.
func (pq PriorityQueue) Less(i, j int) bool { return pq[i].Priority < pq[j].Priority }
// Swap swaps two items in the priority queue.
func (pq PriorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
pq[i].Index = i
pq[j].Index = j
}
// Push adds an item to the priority queue.
func (pq *PriorityQueue) Push(x any) {
item := x.(*Item)
item.Index = len(*pq)
*pq = append(*pq, item)
}
// Pop removes and returns the item with the highest priority (lowest value).
func (pq *PriorityQueue) Pop() any {
old := *pq
n := len(old)
item := old[n-1]
item.Index = -1 // Mark as removed.
*pq = old[0 : n-1]
return item
}
// Graph represents a weighted graph.
type Graph struct {
Edges map[string]map[string]int // Adjacency list to store edges and their weights.
}
// NewGraph creates and initializes a new Graph.
func NewGraph() *Graph {
return &Graph{
Edges: make(map[string]map[string]int),
}
}
// AddNode adds a node to the graph without edges.
func (g *Graph) AddNode(node string) {
if _, exists := g.Edges[node]; !exists {
g.Edges[node] = make(map[string]int) // Initialize an empty adjacency list.
}
}
// AddEdge adds a directed edge to the graph.
func (g *Graph) AddEdge(from, to string, weight int) {
g.AddNode(from)
g.AddNode(to)
g.Edges[from][to] = weight // Add the edge with its weight.
}
// Dijkstra computes shortest paths from the start node to all other nodes.
func (g *Graph) Dijkstra(start string) (map[string]int, map[string]string) {
// Initialize distances map with maximum integer values (infinity).
distances := make(map[string]int)
previous := make(map[string]string) // To reconstruct paths.
for node := range g.Edges {
distances[node] = math.MaxInt
}
distances[start] = 0 // Distance to the start node is 0.
// Initialize the priority queue and add the start node.
pq := &PriorityQueue{}
heap.Push(pq, &Item{Node: start, Priority: 0})
// Process nodes in the priority queue.
for pq.Len() > 0 {
// Extract the node with the smallest distance.
current := heap.Pop(pq).(*Item)
// Update distances to neighboring nodes.
for neighbor, weight := range g.Edges[current.Node] {
newDistance := distances[current.Node] + weight
if newDistance < distances[neighbor] { // If a shorter path is found, update it.
distances[neighbor] = newDistance
previous[neighbor] = current.Node
heap.Push(pq, &Item{Node: neighbor, Priority: newDistance}) // Add or update the neighbor in the queue.
}
}
}
return distances, previous // Return the computed shortest distances and previous nodes for paths.
}
// ReconstructPath reconstructs the shortest path from start to a given target node.
func ReconstructPath(previous map[string]string, start, target string) []string {
path := []string{}
for at := target; at != ""; at = previous[at] {
path = append([]string{at}, path...) // Prepend the node to the path.
if at == start {
break
}
}
if len(path) == 0 || path[0] != start {
return []string{} // Return an empty slice if no path exists.
}
return path
}
// joinPath formats a slice of nodes into a string path like "A -> B -> C".
func joinPath(path []string) string {
return strings.Join(path, " -> ")
}
func main() {
// Create a new graph.
graph := NewGraph()
// Add all nodes explicitly to ensure inclusion of isolated nodes.
graph.AddNode("A")
graph.AddNode("B")
graph.AddNode("C")
graph.AddNode("D")
graph.AddNode("E")
// Add edges to the graph.
graph.AddEdge("A", "B", 1)
graph.AddEdge("A", "C", 4)
graph.AddEdge("B", "C", 2)
graph.AddEdge("B", "D", 6)
graph.AddEdge("C", "D", 3)
graph.AddEdge("C", "E", 5)
graph.AddEdge("D", "E", 1)
// Compute shortest distances and paths from node "A".
distances, previous := graph.Dijkstra("A")
// Collect all nodes and sort them.
nodes := make([]string, 0, len(graph.Edges))
for node := range graph.Edges {
nodes = append(nodes, node)
}
sort.Strings(nodes) // Sort nodes alphabetically.
// Print the shortest distances from node "A".
fmt.Println("Shortest distances from A:")
for _, node := range nodes {
distance := distances[node]
if distance == math.MaxInt {
fmt.Printf("To %s: unreachable\n", node) // Print unreachable nodes.
} else {
fmt.Printf("To %s: %d\n", node, distance) // Print the shortest distance.
}
}
// Print the shortest paths from node "A".
fmt.Println("\nShortest paths from A:")
for _, node := range nodes {
path := ReconstructPath(previous, "A", node)
if len(path) > 0 {
fmt.Printf("To %s: %s\n", node, joinPath(path))
} else {
fmt.Printf("To %s: No path found\n", node)
}
}
}