package main import ( "container/heap" "fmt" ) type Edge struct { dest, weight int } type Item struct { node, dist int } type PriorityQueue []Item func (pq PriorityQueue) Len() int { return len(pq) } func (pq PriorityQueue) Less(i, j int) bool { return pq[i].dist < pq[j].dist } func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] } func (pq *PriorityQueue) Push(x interface{}) { *pq = append(*pq, x.(Item)) } func (pq *PriorityQueue) Pop() interface{} { old := *pq n := len(old) item := old[n-1] *pq = old[0 : n-1] return item } func dijkstra(graph [][]Edge, start int) []int { dist := make([]int, len(graph)) for i := range dist { dist[i] = 1 << 30 } dist[start] = 0 pq := &PriorityQueue{} heap.Init(pq) heap.Push(pq, Item{start, 0}) for pq.Len() > 0 { item := heap.Pop(pq).(Item) node, curDist := item.node, item.dist if curDist > dist[node] { continue } for _, e := range graph[node] { newDist := curDist + e.weight if newDist < dist[e.dest] { dist[e.dest] = newDist heap.Push(pq, Item{e.dest, newDist}) } } } return dist } func main() { graph := [][]Edge{ {{1, 4}, {2, 1}}, {{3, 1}}, {{1, 2}, {3, 5}}, {}, } dist := dijkstra(graph, 0) fmt.Println(dist) }
The shortest distances from node 0 are: to node 0 is 0, to node 2 is 1, to node 1 is 3 (via node 2), and to node 3 is 4 (via node 1).
Dijkstra's algorithm finalizes the shortest distance to a node once it is visited. Negative edges can create shorter paths after this finalization, which the algorithm cannot update.
package main import ( "container/heap" "fmt" ) type Edge struct { dest, weight int } type Item struct { node, dist int } type PriorityQueue []Item func (pq PriorityQueue) Len() int { return len(pq) } func (pq PriorityQueue) Less(i, j int) bool { return pq[i].dist < pq[j].dist } func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] } func (pq *PriorityQueue) Push(x interface{}) { *pq = append(*pq, x.(Item)) } func (pq *PriorityQueue) Pop() interface{} { old := *pq n := len(old) item := old[n-1] *pq = old[0 : n-1] return item } func dijkstra(graph [][]Edge, start int) []int { dist := make([]int, len(graph)) for i := range dist { dist[i] = 1 << 30 } dist[start] = 0 pq := &PriorityQueue{} heap.Init(pq) heap.Push(pq, Item{start, 0}) for pq.Len() > 0 { item := heap.Pop(pq).(Item) node, curDist := item.node, item.dist if curDist > dist[node] { continue } for _, e := range graph[node] { newDist := curDist + e.weight if newDist < dist[e.dest] { dist[e.dest] = newDist heap.Push(pq, Item{e.dest, newDist}) } } } return dist } func main() { graph := [][]Edge{{{1, 2}}, {{2, 3}}, {}} dist := dijkstra(graph, 0) fmt.Println(dist) }
The code correctly implements Dijkstra's algorithm and will output the shortest distances [0, 2, 5]. No runtime or panic errors occur.
package main import ( "container/heap" "fmt" ) type Edge struct { dest, weight int } type Item struct { node, dist int } type PriorityQueue []Item func (pq PriorityQueue) Len() int { return len(pq) } func (pq PriorityQueue) Less(i, j int) bool { return pq[i].dist < pq[j].dist } func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] } func (pq *PriorityQueue) Push(x interface{}) { *pq = append(*pq, x.(Item)) } func (pq *PriorityQueue) Pop() interface{} { old := *pq n := len(old) item := old[n-1] *pq = old[0 : n-1] return item } func dijkstra(graph [][]Edge, start int) []int { dist := make([]int, len(graph)) for i := range dist { dist[i] = 1 << 30 } dist[start] = 0 pq := &PriorityQueue{} heap.Init(pq) heap.Push(pq, Item{start, 0}) for pq.Len() > 0 { item := heap.Pop(pq).(Item) node, curDist := item.node, item.dist if curDist > dist[node] { continue } for _, e := range graph[node] { newDist := curDist + e.weight if newDist < dist[e.dest] { dist[e.dest] = newDist heap.Push(pq, Item{e.dest, newDist}) } } } return dist } func main() { graph := [][]Edge{ {{1, 1}, {2, 4}}, {{2, 2}, {3, 6}}, {{3, 3}}, {}, } dist := dijkstra(graph, 0) fmt.Println(dist) }
The shortest distances are 0 to 0 is 0, to 1 is 1, to 2 is 3 (via 1), and to 3 is 6 (via 2).
Each vertex is inserted and extracted from the priority queue once, and each edge may cause an update. Using a binary heap, each operation takes O(log V), so total is O((V + E) log V).
