// Builds a BFS tree (as a directed graph) from the given graph and start node. func BFSTree(g graph.Graph, start graph.Node) *simple.DirectedGraph { if !g.Has(start) { panic(fmt.Sprintf("BFSTree: Start node %r not in graph %r", start, g)) } ret := simple.NewDirectedGraph(0.0, math.Inf(1)) seen := make(map[int]bool) q := queue.New() q.Add(start) ret.AddNode(simple.Node(start.ID())) for q.Length() > 0 { node := q.Peek().(graph.Node) q.Remove() for _, neighbor := range g.From(node) { if !seen[neighbor.ID()] { seen[neighbor.ID()] = true ret.AddNode(simple.Node(neighbor.ID())) ret.SetEdge(simple.Edge{F: simple.Node(node.ID()), T: simple.Node(neighbor.ID()), W: g.Edge(node, neighbor).Weight()}) q.Add(neighbor) } } } return ret }
// 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|+|V|.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. // // 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] { path.dist[k] = mid.dist } 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 }
// IsPathIn returns whether path is a path in g. // // As special cases, IsPathIn returns true for a zero length path or for // a path of length 1 when the node in path exists in the graph. func IsPathIn(g graph.Graph, path []graph.Node) bool { switch len(path) { case 0: return true case 1: return g.Has(path[0]) default: var canReach func(u, v graph.Node) bool switch g := g.(type) { case graph.Directed: canReach = g.HasEdgeFromTo default: canReach = g.HasEdgeBetween } for i, u := range path[:len(path)-1] { if !canReach(u, path[i+1]) { return false } } return true } }
// BellmanFordFrom returns a shortest-path tree for a shortest path from u to all nodes in // the graph g, or false indicating that a negative cycle exists in the graph. If the graph // does not implement graph.Weighter, UniformCost is used. // // The time complexity of BellmanFordFrom is O(|V|.|E|). func BellmanFordFrom(u graph.Node, g graph.Graph) (path Shortest, ok bool) { if !g.Has(u) { return Shortest{from: u}, true } var weight Weighting if wg, ok := g.(graph.Weighter); ok { weight = wg.Weight } else { weight = UniformCost(g) } nodes := g.Nodes() path = newShortestFrom(u, nodes) path.dist[path.indexOf[u.ID()]] = 0 // TODO(kortschak): Consider adding further optimisations // from http://arxiv.org/abs/1111.5414. for i := 1; i < len(nodes); i++ { changed := false for j, u := range nodes { for _, v := range g.From(u) { k := path.indexOf[v.ID()] w, ok := weight(u, v) if !ok { panic("bellman-ford: unexpected invalid weight") } joint := path.dist[j] + w if joint < path.dist[k] { path.set(k, joint, j) changed = true } } } if !changed { break } } for j, u := range nodes { for _, v := range g.From(u) { k := path.indexOf[v.ID()] w, ok := weight(u, v) if !ok { panic("bellman-ford: unexpected invalid weight") } if path.dist[j]+w < path.dist[k] { return path, false } } } return path, true }