| // Copyright ©2014 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" |
| "math" |
| "sort" |
| |
| "gonum.org/v1/gonum/graph" |
| "gonum.org/v1/gonum/graph/simple" |
| ) |
| |
| // WeightedBuilder is a type that can add nodes and weighted edges. |
| type WeightedBuilder interface { |
| AddNode(graph.Node) |
| SetWeightedEdge(graph.WeightedEdge) |
| } |
| |
| // Prim generates a minimum spanning tree of g by greedy tree extension, placing |
| // the result in the destination, dst. If the edge weights of g are distinct |
| // it will be the unique minimum spanning tree of g. The destination is not cleared |
| // first. The weight of the minimum spanning tree is returned. If g is not connected, |
| // a minimum spanning forest will be constructed in dst and the sum of minimum |
| // spanning tree weights will be returned. |
| // |
| // Nodes and Edges from g are used to construct dst, so if the Node and Edge |
| // types used in g are pointer or reference-like, then the values will be shared |
| // between the graphs. |
| // |
| // If dst has nodes that exist in g, Prim will panic. |
| func Prim(dst WeightedBuilder, g graph.WeightedUndirected) float64 { |
| nodes := graph.NodesOf(g.Nodes()) |
| if len(nodes) == 0 { |
| return 0 |
| } |
| |
| q := &primQueue{ |
| indexOf: make(map[int64]int, len(nodes)-1), |
| nodes: make([]simple.WeightedEdge, 0, len(nodes)-1), |
| } |
| dst.AddNode(nodes[0]) |
| for _, u := range nodes[1:] { |
| dst.AddNode(u) |
| heap.Push(q, simple.WeightedEdge{F: u, W: math.Inf(1)}) |
| } |
| |
| u := nodes[0] |
| uid := u.ID() |
| for _, v := range graph.NodesOf(g.From(uid)) { |
| w, ok := g.Weight(uid, v.ID()) |
| if !ok { |
| panic("prim: unexpected invalid weight") |
| } |
| q.update(v, u, w) |
| } |
| |
| var w float64 |
| for q.Len() > 0 { |
| e := heap.Pop(q).(simple.WeightedEdge) |
| if e.To() != nil && g.HasEdgeBetween(e.From().ID(), e.To().ID()) { |
| dst.SetWeightedEdge(g.WeightedEdge(e.From().ID(), e.To().ID())) |
| w += e.Weight() |
| } |
| |
| u = e.From() |
| uid := u.ID() |
| for _, n := range graph.NodesOf(g.From(uid)) { |
| if key, ok := q.key(n); ok { |
| w, ok := g.Weight(uid, n.ID()) |
| if !ok { |
| panic("prim: unexpected invalid weight") |
| } |
| if w < key { |
| q.update(n, u, w) |
| } |
| } |
| } |
| } |
| return w |
| } |
| |
| // primQueue is a Prim's priority queue. The priority queue is a |
| // queue of edge From nodes keyed on the minimum edge weight to |
| // a node in the set of nodes already connected to the minimum |
| // spanning forest. |
| type primQueue struct { |
| indexOf map[int64]int |
| nodes []simple.WeightedEdge |
| } |
| |
| func (q *primQueue) Less(i, j int) bool { |
| return q.nodes[i].Weight() < q.nodes[j].Weight() |
| } |
| |
| func (q *primQueue) Swap(i, j int) { |
| q.indexOf[q.nodes[i].From().ID()] = j |
| q.indexOf[q.nodes[j].From().ID()] = i |
| q.nodes[i], q.nodes[j] = q.nodes[j], q.nodes[i] |
| } |
| |
| func (q *primQueue) Len() int { |
| return len(q.nodes) |
| } |
| |
| func (q *primQueue) Push(x interface{}) { |
| n := x.(simple.WeightedEdge) |
| q.indexOf[n.From().ID()] = len(q.nodes) |
| q.nodes = append(q.nodes, n) |
| } |
| |
| func (q *primQueue) Pop() interface{} { |
| n := q.nodes[len(q.nodes)-1] |
| q.nodes = q.nodes[:len(q.nodes)-1] |
| delete(q.indexOf, n.From().ID()) |
| return n |
| } |
| |
| // key returns the key for the node u and whether the node is |
| // in the queue. If the node is not in the queue, key is returned |
| // as +Inf. |
| func (q *primQueue) key(u graph.Node) (key float64, ok bool) { |
| i, ok := q.indexOf[u.ID()] |
| if !ok { |
| return math.Inf(1), false |
| } |
| return q.nodes[i].Weight(), ok |
| } |
| |
| // update updates u's position in the queue with the new closest |
| // MST-connected neighbour, v, and the key weight between u and v. |
| func (q *primQueue) update(u, v graph.Node, key float64) { |
| id := u.ID() |
| i, ok := q.indexOf[id] |
| if !ok { |
| return |
| } |
| q.nodes[i].T = v |
| q.nodes[i].W = key |
| heap.Fix(q, i) |
| } |
| |
| // UndirectedWeightLister is an undirected graph that returns edge weights and |
| // the set of edges in the graph. |
| type UndirectedWeightLister interface { |
| graph.WeightedUndirected |
| WeightedEdges() graph.WeightedEdges |
| } |
| |
| // Kruskal generates a minimum spanning tree of g by greedy tree coalescence, placing |
| // the result in the destination, dst. If the edge weights of g are distinct |
| // it will be the unique minimum spanning tree of g. The destination is not cleared |
| // first. The weight of the minimum spanning tree is returned. If g is not connected, |
| // a minimum spanning forest will be constructed in dst and the sum of minimum |
| // spanning tree weights will be returned. |
| // |
| // Nodes and Edges from g are used to construct dst, so if the Node and Edge |
| // types used in g are pointer or reference-like, then the values will be shared |
| // between the graphs. |
| // |
| // If dst has nodes that exist in g, Kruskal will panic. |
| func Kruskal(dst WeightedBuilder, g UndirectedWeightLister) float64 { |
| edges := graph.WeightedEdgesOf(g.WeightedEdges()) |
| sort.Sort(byWeight(edges)) |
| |
| ds := make(djSet) |
| it := g.Nodes() |
| for it.Next() { |
| n := it.Node() |
| dst.AddNode(n) |
| ds.add(n.ID()) |
| } |
| |
| var w float64 |
| for _, e := range edges { |
| if s1, s2 := ds.find(e.From().ID()), ds.find(e.To().ID()); s1 != s2 { |
| ds.union(s1, s2) |
| dst.SetWeightedEdge(g.WeightedEdge(e.From().ID(), e.To().ID())) |
| w += e.Weight() |
| } |
| } |
| return w |
| } |
| |
| type byWeight []graph.WeightedEdge |
| |
| func (e byWeight) Len() int { return len(e) } |
| func (e byWeight) Less(i, j int) bool { return e[i].Weight() < e[j].Weight() } |
| func (e byWeight) Swap(i, j int) { e[i], e[j] = e[j], e[i] } |