mirror of
https://github.com/gonum/gonum.git
synced 2025-10-23 07:09:27 +08:00
graph: imported graph as a subtree
This commit is contained in:
147
graph/path/dijkstra.go
Normal file
147
graph/path/dijkstra.go
Normal file
@@ -0,0 +1,147 @@
|
||||
// Copyright ©2015 The gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package path
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
|
||||
"github.com/gonum/graph"
|
||||
)
|
||||
|
||||
// DijkstraFrom returns a shortest-path tree for a shortest path from u to all nodes in
|
||||
// the graph g. If the graph does not implement graph.Weighter, UniformCost is used.
|
||||
// DijkstraFrom will panic if g has a u-reachable negative edge weight.
|
||||
//
|
||||
// The time complexity of DijkstrFrom is O(|E|.log|V|).
|
||||
func DijkstraFrom(u graph.Node, g graph.Graph) Shortest {
|
||||
if !g.Has(u) {
|
||||
return Shortest{from: u}
|
||||
}
|
||||
var weight Weighting
|
||||
if wg, ok := g.(graph.Weighter); ok {
|
||||
weight = wg.Weight
|
||||
} else {
|
||||
weight = UniformCost(g)
|
||||
}
|
||||
|
||||
nodes := g.Nodes()
|
||||
path := newShortestFrom(u, nodes)
|
||||
|
||||
// Dijkstra's algorithm here is implemented essentially as
|
||||
// described in Function B.2 in figure 6 of UTCS Technical
|
||||
// Report TR-07-54.
|
||||
//
|
||||
// This implementation deviates from the report as follows:
|
||||
// - the value of path.dist for the start vertex u is initialized to 0;
|
||||
// - outdated elements from the priority queue (i.e. with respect to the dist value)
|
||||
// are skipped.
|
||||
//
|
||||
// http://www.cs.utexas.edu/ftp/techreports/tr07-54.pdf
|
||||
Q := priorityQueue{{node: u, dist: 0}}
|
||||
for Q.Len() != 0 {
|
||||
mid := heap.Pop(&Q).(distanceNode)
|
||||
k := path.indexOf[mid.node.ID()]
|
||||
if mid.dist > path.dist[k] {
|
||||
continue
|
||||
}
|
||||
for _, v := range g.From(mid.node) {
|
||||
j := path.indexOf[v.ID()]
|
||||
w, ok := weight(mid.node, v)
|
||||
if !ok {
|
||||
panic("dijkstra: unexpected invalid weight")
|
||||
}
|
||||
if w < 0 {
|
||||
panic("dijkstra: negative edge weight")
|
||||
}
|
||||
joint := path.dist[k] + w
|
||||
if joint < path.dist[j] {
|
||||
heap.Push(&Q, distanceNode{node: v, dist: joint})
|
||||
path.set(j, joint, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
// DijkstraAllPaths returns a shortest-path tree for shortest paths in the graph g.
|
||||
// If the graph does not implement graph.Weighter, UniformCost is used.
|
||||
// DijkstraAllPaths will panic if g has a negative edge weight.
|
||||
//
|
||||
// The time complexity of DijkstrAllPaths is O(|V|.|E|+|V|^2.log|V|).
|
||||
func DijkstraAllPaths(g graph.Graph) (paths AllShortest) {
|
||||
paths = newAllShortest(g.Nodes(), false)
|
||||
dijkstraAllPaths(g, paths)
|
||||
return paths
|
||||
}
|
||||
|
||||
// dijkstraAllPaths is the all-paths implementation of Dijkstra. It is shared
|
||||
// between DijkstraAllPaths and JohnsonAllPaths to avoid repeated allocation
|
||||
// of the nodes slice and the indexOf map. It returns nothing, but stores the
|
||||
// result of the work in the paths parameter which is a reference type.
|
||||
func dijkstraAllPaths(g graph.Graph, paths AllShortest) {
|
||||
var weight Weighting
|
||||
if wg, ok := g.(graph.Weighter); ok {
|
||||
weight = wg.Weight
|
||||
} else {
|
||||
weight = UniformCost(g)
|
||||
}
|
||||
|
||||
var Q priorityQueue
|
||||
for i, u := range paths.nodes {
|
||||
// Dijkstra's algorithm here is implemented essentially as
|
||||
// described in Function B.2 in figure 6 of UTCS Technical
|
||||
// Report TR-07-54 with the addition of handling multiple
|
||||
// co-equal paths.
|
||||
//
|
||||
// http://www.cs.utexas.edu/ftp/techreports/tr07-54.pdf
|
||||
|
||||
// Q must be empty at this point.
|
||||
heap.Push(&Q, distanceNode{node: u, dist: 0})
|
||||
for Q.Len() != 0 {
|
||||
mid := heap.Pop(&Q).(distanceNode)
|
||||
k := paths.indexOf[mid.node.ID()]
|
||||
if mid.dist < paths.dist.At(i, k) {
|
||||
paths.dist.Set(i, k, mid.dist)
|
||||
}
|
||||
for _, v := range g.From(mid.node) {
|
||||
j := paths.indexOf[v.ID()]
|
||||
w, ok := weight(mid.node, v)
|
||||
if !ok {
|
||||
panic("dijkstra: unexpected invalid weight")
|
||||
}
|
||||
if w < 0 {
|
||||
panic("dijkstra: negative edge weight")
|
||||
}
|
||||
joint := paths.dist.At(i, k) + w
|
||||
if joint < paths.dist.At(i, j) {
|
||||
heap.Push(&Q, distanceNode{node: v, dist: joint})
|
||||
paths.set(i, j, joint, k)
|
||||
} else if joint == paths.dist.At(i, j) {
|
||||
paths.add(i, j, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type distanceNode struct {
|
||||
node graph.Node
|
||||
dist float64
|
||||
}
|
||||
|
||||
// priorityQueue implements a no-dec priority queue.
|
||||
type priorityQueue []distanceNode
|
||||
|
||||
func (q priorityQueue) Len() int { return len(q) }
|
||||
func (q priorityQueue) Less(i, j int) bool { return q[i].dist < q[j].dist }
|
||||
func (q priorityQueue) Swap(i, j int) { q[i], q[j] = q[j], q[i] }
|
||||
func (q *priorityQueue) Push(n interface{}) { *q = append(*q, n.(distanceNode)) }
|
||||
func (q *priorityQueue) Pop() interface{} {
|
||||
t := *q
|
||||
var n interface{}
|
||||
n, *q = t[len(t)-1], t[:len(t)-1]
|
||||
return n
|
||||
}
|
Reference in New Issue
Block a user