package main import ( "container/heap" "fmt" "math" ) type Edge struct { To int Weight int } type item struct { vertex int distance int } type priorityQueue []item func (pq priorityQueue) Len() int { return len(pq) } func (pq priorityQueue) Less(i, j int) bool { return pq[i].distance < pq[j].distance } func (pq priorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] } func (pq *priorityQueue) Push(x any) { *pq = append(*pq, x.(item)) } func (pq *priorityQueue) Pop() any { old := *pq x := old[len(old)-1] *pq = old[:len(old)-1] return x } // Dijkstra returns distances and predecessors from source. // Unreachable vertices have distance math.MaxInt and predecessor -1. func Dijkstra(graph [][]Edge, source int) ([]int, []int, error) { if source < 0 || source >= len(graph) { return nil, nil, fmt.Errorf("source vertex %d is out of range", source) } dist := make([]int, len(graph)) prev := make([]int, len(graph)) for i := range graph { dist[i] = math.MaxInt prev[i] = -1 } dist[source] = 0 pq := &priorityQueue{{vertex: source, distance: 0}} heap.Init(pq) for pq.Len() > 0 { current := heap.Pop(pq).(item) if current.distance != dist[current.vertex] { continue // Ignore stale queue entries. } for _, edge := range graph[current.vertex] { if edge.To < 0 || edge.To >= len(graph) { return nil, nil, fmt.Errorf("edge target %d is out of range", edge.To) } if edge.Weight < 0 { return nil, nil, fmt.Errorf("negative edge weight from %d to %d", current.vertex, edge.To) } if dist[current.vertex] > math.MaxInt-edge.Weight { continue // Prevent integer overflow. } candidate := dist[current.vertex] + edge.Weight if candidate < dist[edge.To] { dist[edge.To] = candidate prev[edge.To] = current.vertex heap.Push(pq, item{vertex: edge.To, distance: candidate}) } } } return dist, prev, nil } // Path reconstructs a source-to-target path from Dijkstra's predecessors. func Path(prev []int, source, target int) []int { if source < 0 || source >= len(prev) || target < 0 || target >= len(prev) { return nil } var reversed []int for vertex := target; vertex != -1; vertex = prev[vertex] { reversed = append(reversed, vertex) if vertex == source { path := make([]int, len(reversed)) for i := range reversed { path[len(reversed)-1-i] = reversed[i] } return path } } return nil } func main() { // Test shortest distances and path reconstruction. graph := [][]Edge{ {{To: 1, Weight: 4}, {To: 2, Weight: 1}}, {{To: 3, Weight: 1}}, {{To: 1, Weight: 2}, {To: 3, Weight: 5}}, nil, {{To: 0, Weight: 1}}, } dist, prev, err := Dijkstra(graph, 0) if err != nil { panic(err) } assert(equal(dist, []int{0, 3, 1, 4, math.MaxInt}), "unexpected distances") assert(equal(Path(prev, 0, 3), []int{0, 2, 1, 3}), "unexpected path") // Test rejection of negative weights. _, _, err = Dijkstra([][]Edge{{{To: 1, Weight: -1}}, nil}, 0) assert(err != nil, "expected a negative-weight error") } func equal(a, b []int) bool { if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return true } func assert(condition bool, message string) { if !condition { panic(message) } }