//输入邻接表,返回最小生成树的权。 //复杂度为O(E+VlogV),通常比Kruskal强。 //对有向图不适用,多路同权时选择有问题(不能倒着用,可能选错)。 func Prim(roads [][]graph.Path) (sum uint, fail bool) { var size = len(roads) sum = uint(0) if size < 2 { return 0, true } const FAKE = -1 var list = graph.NewVector(size) for i := 1; i < size; i++ { list[i].Link = FAKE } list[0].Index, list[0].Link, list[0].Dist = 0, 0, 0 var root = graph.Insert(nil, &list[0]) var cnt int for cnt = 0; root != nil; cnt++ { var current = root root = graph.Extract(root) sum += current.Dist for _, path := range roads[current.Index] { var peer = &list[path.Next] if peer.Link == FAKE { //未涉及点 peer.Index, peer.Link, peer.Dist = path.Next, current.Index, path.Dist root = graph.Insert(root, peer) } else if peer.Index != FAKE && //外围点 path.Dist < peer.Dist { //可更新 peer.Link = current.Index root = graph.FloatUp(root, peer, path.Dist) } } current.Index = FAKE //入围 } return sum, cnt != size }
//输入邻接表,返回两点间的最短路径及其长度(-1指不通)。 func DijkstraPath(roads [][]graph.Path, start int, end int) ( Dist int, marks []int) { var size = len(roads) if start < 0 || end < 0 || start >= size || end >= size { return -1, []int{} } if start == end { return 0, []int{start} } const FAKE = -1 var list = graph.NewVector(size) for i := 0; i < size; i++ { list[i].Link = FAKE } list[start].Index, list[start].Link, list[start].Dist = start, start, 0 var root = graph.Insert(nil, &list[start]) for root != nil && root.Dist != graph.MaxDistance { var current = root if current.Index == end { for idx := end; idx != start; idx = list[idx].Link { marks = append(marks, idx) } marks = append(marks, start) for left, right := 0, len(marks)-1; left < right; { marks[left], marks[right] = marks[right], marks[left] left++ right-- } return (int)(current.Dist), marks } root = graph.Extract(root) for _, path := range roads[current.Index] { var peer = &list[path.Next] if peer.Link == FAKE { //未涉及点 peer.Index, peer.Link = path.Next, current.Index peer.Dist = current.Dist + path.Dist root = graph.Insert(root, peer) } else if peer.Index != FAKE { //外围点 var distance = current.Dist + path.Dist if distance < peer.Dist { peer.Link = current.Index root = graph.FloatUp(root, peer, distance) } } } current.Index = FAKE //入围 } return -1, []int{} }
//输入邻接表,返回某点到各点的最短路径的长度(-1指不通)。 //复杂度为O(E+VlogV),已知最快的单源最短路径算法,对稀疏图尤甚。 //可以处理有向图,不能处理负权边。 func Dijkstra(roads [][]graph.Path, start int) []int { var size = len(roads) if size == 0 || start < 0 || start >= size { return []int{} } var result = make([]int, size) if size == 1 { result[0] = 0 return result } const FAKE = -1 var list = graph.NewVector(size) for i := 0; i < size; i++ { list[i].Link = FAKE } list[start].Index, list[start].Link, list[start].Dist = start, start, 0 var root = graph.Insert(nil, &list[start]) for root != nil && root.Dist != graph.MaxDistance { var current = root root = graph.Extract(root) for _, path := range roads[current.Index] { var peer = &list[path.Next] if peer.Link == FAKE { //未涉及点 peer.Index, peer.Link = path.Next, current.Index peer.Dist = current.Dist + path.Dist root = graph.Insert(root, peer) } else if peer.Index != FAKE { //外围点 var distance = current.Dist + path.Dist if distance < peer.Dist { //peer.Link = current.Index root = graph.FloatUp(root, peer, distance) } } } current.Index = FAKE //入围 } for i := 0; i < size; i++ { if list[i].Dist == graph.MaxDistance { result[i] = -1 } else { result[i] = (int)(list[i].Dist) } } return result }
//输入邻接表,返回一个以0号节点为根的最小生成树。 func PrimTree(roads [][]graph.Path) ([]Edge, error) { var size = len(roads) if size < 2 { return []Edge{}, errors.New("illegal input") } var edges = make([]Edge, 0, size-1) const FAKE = -1 var list = graph.NewVector(size) for i := 1; i < size; i++ { list[i].Link = FAKE } list[0].Index, list[0].Link, list[0].Dist = 0, 0, 0 var root = graph.Insert(nil, &list[0]) for { var current = root root = graph.Extract(root) for _, path := range roads[current.Index] { var peer = &list[path.Next] if peer.Link == FAKE { //未涉及点 peer.Index, peer.Link, peer.Dist = path.Next, current.Index, path.Dist root = graph.Insert(root, peer) } else if peer.Index != FAKE && //外围点 path.Dist < peer.Dist { //可更新 peer.Link = current.Index root = graph.FloatUp(root, peer, path.Dist) } } current.Index = FAKE //入围 if root == nil { break } edges = append(edges, Edge{root.Link, root.Index}) } if len(edges) != size-1 { return edges, errors.New("isolated part exist") } return edges, nil }