04.图的最小生成普利姆和克努斯卡尔
作者:互联网
一、克努斯卡尔的代码实现
1. 图的邻接矩阵实现
mgraph.go
package graph
import "errors"
const MaxSize = 20
type MGraph struct {
Edges [MaxSize][MaxSize]int
EdgeNum int
Nodes []string
Indexs map[string]int
}
type Edge struct {
NodeStart, NodeEnd string
Val int
}
func NewMGraph() MGraph {
var g MGraph
for k, v := range g.Edges {
for kk, _ := range v {
g.Edges[k][kk] = -1
}
}
g.Indexs = make(map[string]int)
return g
}
func (g *MGraph) AddNode(nodeName string) error {
if g.Indexs == nil {
return errors.New("不是有效的图")
}
if _, ok := g.Indexs[nodeName]; ok {
return errors.New("已经添加过此结点")
}
g.Indexs[nodeName] = len(g.Nodes)
g.Nodes = append(g.Nodes, nodeName)
return nil
}
func (g *MGraph) AddEdge(nodeName1, nodeName2 string, val int) error {
if _, ok := g.Indexs[nodeName1]; !ok {
return errors.New("结点不存在:" + nodeName1)
}
if _, ok := g.Indexs[nodeName2]; !ok {
return errors.New("结点不存在:" + nodeName2)
}
if g.Edges[g.Indexs[nodeName1]][g.Indexs[nodeName2]] != -1 {
return errors.New("边已经存在")
}
g.Edges[g.Indexs[nodeName1]][g.Indexs[nodeName2]] = val
g.Edges[g.Indexs[nodeName2]][g.Indexs[nodeName1]] = val
g.EdgeNum++
return nil
}
func (g *MGraph) GetEdgeList() []Edge {
var edgeList []Edge
for i := 0; i < len(g.Nodes); i++ {
for j := 0; j < i; j++ {
if g.Edges[i][j] >= 0 {
edgeList = append(
edgeList,
Edge{NodeStart: g.Nodes[i], NodeEnd: g.Nodes[j], Val: g.Edges[i][j]},
)
}
}
}
return edgeList
}
2.克努斯卡尔实现代码
Kruskal.go
package minimum_spanning_tree
import (
"fmt"
"gitee.com/gudongkun/datestruct/dataStructures/graph"
"gitee.com/gudongkun/datestruct/dataStructures/unionfind"
"sort"
)
func GetExGraph() *graph.MGraph {
g := graph.NewMGraph()
g.AddNode("A")
g.AddNode("B")
g.AddNode("C")
g.AddNode("D")
g.AddNode("E")
g.AddNode("F")
g.AddNode("G")
g.AddNode("H")
g.AddNode("I")
g.AddNode("J")
g.AddEdge("A", "B", 5)
g.AddEdge("A", "D", 9)
g.AddEdge("A", "E", 1)
g.AddEdge("B", "D", 2)
g.AddEdge("B", "C", 4)
g.AddEdge("C", "H", 4)
g.AddEdge("C", "I", 1)
g.AddEdge("C", "J", 8)
g.AddEdge("D", "H", 2)
g.AddEdge("D", "G", 11)
g.AddEdge("D", "F", 5)
g.AddEdge("D", "E", 2)
g.AddEdge("E", "F", 1)
g.AddEdge("F", "G", 7)
g.AddEdge("G", "H", 1)
g.AddEdge("G", "I", 4)
g.AddEdge("H", "I", 6)
g.AddEdge("I", "J", 0)
return &g
}
func Kruskal() []graph.Edge {
var treeEdge []graph.Edge
g := GetExGraph()
originEdge := g.GetEdgeList()
//1.给边排序
sort.SliceStable(originEdge, func(i, j int) bool {
return originEdge[i].Val < originEdge[j].Val
})
//2.构建并查集
union := unionfind.NewUnionFind(g.Nodes)
for _, v := range originEdge {
if union.IsConnected(v.NodeStart, v.NodeEnd) {
continue
}
treeEdge = append(treeEdge, v)
union.Unify(v.NodeStart, v.NodeEnd)
if union.Groups() == 1 {
break
}
}
if union.Groups()>1 {
fmt.Println("非联通图",union)
} else {
fmt.Println("生成树为:",treeEdge)
}
return treeEdge
}
3.测试代码
Kruskal_test.go
package minimum_spanning_tree
import "testing"
func TestGetExGraph(t *testing.T) {
g := GetExGraph()
if g.EdgeNum != 18 {
t.Error("GetExGraph Error")
}
if len(g.GetEdgeList()) != 18 {
t.Error("GetEdgeList Error")
}
}
func TestKruskal(t *testing.T) {
Kruskal()
}
二、图的普利姆算法
算法思想:在图中任意选出一个顶点作为一颗树,选出和这个顶点相连的最短路径,把路径加入树中,边加入选择的边中,此时得到了一颗有两个边的树。然后再从与这颗树相连的边中中选出最短的边,把他的另外一个顶点加入树,边加入选择边,依次类推,直到所有顶点加入到树中。此时得到的树就是最小生成树。
1、算法描述
-
初始化 三个数组
seleted:记录节点是否再已经再生成树中,下标代表节点,初始都为false;
minDist: 记录某一时刻,这个点到 已选顶点的集合的 最小权边的权值大小,初始为无穷大
prev :记录 顶点连再树中的父元素的下标。初始都为-1,代表不连接任何顶点。 -
初始化任意顶点s操作:
任意选择一个顶点s,添加到生成树中(seleted[s] = true);
点s已经进入到生成树中,到生成树的距离已经没有意义,设置为-1(minDist[s] = -1);
初始顶点没有,父元素(prev[s] = -1) -
更新第一个顶点s操作:
扫描所有与顶点s 连接的边(cp另外一个顶点,v边的权重值),如果顶点到这个店的距离,比minDist中第小,minDist[cp] = v ,prev[cp] = s -
扫描:
扫描所有未选中结点(seleted中值为false的点),找到minDist 值最小的点p -
添加:
添加点p到生成树中(seleted[p] = true)
p点到树的距离,失去意义设置为-1(minDist[p] = -1) -
更新:
扫描所有与顶点p 连接的边(cp边的另外一个顶点,v边的权重值),如果顶点到这个店的距离,比minDist中第小,minDist[cp] = v ,prev[cp] = p -
循环执行 4.扫描、5.添加、6.更新 ,一直到seleted中
-
所有的点都变成 true,
通过prev数组,就能推倒出最小生成树的边。
2、图形描述
图形演示请参考:https://www.bilibili.com/video/BV1Eb41177d1
3、算法代码实现
(1)图增加获取相邻结点接口
func (g *MGraph) GetConnectedEdges(ele string) []Edge {
var edgeList []Edge
k := g.Indexs[ele]
for i := 0; i < len(g.Nodes); i++ {
if g.Edges[k][i] >= 0 {
edgeList = append(
edgeList,
Edge{NodeStart: g.Nodes[k], NodeEnd: g.Nodes[i], Val: g.Edges[k][i]},
)
}
}
return edgeList
}
(2)普利姆算法
prim.go
package minimum_spanning_tree
import "fmt"
func Prim() map[string]string {
g := GetExGraph()
//初始化
selected := make(map[string]bool)
minDist := make(map[string]int)
prev := make(map[string]string)
for _, node := range g.Nodes {
selected[node] = false
minDist[node] = 9999999
}
// 添加第一个顶点
selected[g.Nodes[0]] = true
minDist[g.Nodes[0]] = -1
prev[g.Nodes[0]] = "root"
// 更新第一个顶点
for _, edge := range g.GetConnectedEdges(g.Nodes[0]) {
if minDist[edge.NodeEnd] > edge.Val {
minDist[edge.NodeEnd] = edge.Val
prev[edge.NodeEnd] = g.Nodes[0]
}
}
for {
// scan操作
minK := ""
minV := 9999999
for k, v := range selected {
if v {
continue
}
if minDist[k] < minV { //选出未选中的点中,距离最小的点
minK = k
minV = minDist[k]
}
}
if minK == "" {
break
}
//add 操作
selected[minK] = true // minK 设置为已选择
minDist[minK] = -1 // minK 的距离失效
//更新操作,更新未选中点到 selected 的距离
for _, edge := range g.GetConnectedEdges(minK) {
if minDist[edge.NodeEnd] > edge.Val {
minDist[edge.NodeEnd] = edge.Val
prev[edge.NodeEnd] = minK
}
}
}
fmt.Println(prev, len(prev))
return prev
}
(3) 普利姆算法单元测试
package minimum_spanning_tree
import "testing"
func TestPrim(t *testing.T) {
prev := Prim()
if len(prev) != 10 {
t.Error(prev)
}
}
(4)修改后图的完整代码
package graph
import "errors"
const MaxSize = 20
type MGraph struct {
Edges [MaxSize][MaxSize]int
EdgeNum int
Nodes []string
Indexs map[string]int
}
type Edge struct {
NodeStart, NodeEnd string
Val int
}
func NewMGraph() MGraph {
var g MGraph
for k, v := range g.Edges {
for kk, _ := range v {
g.Edges[k][kk] = -1
}
}
g.Indexs = make(map[string]int)
return g
}
func (g *MGraph) AddNode(nodeName string) error {
if g.Indexs == nil {
return errors.New("不是有效的图")
}
if _, ok := g.Indexs[nodeName]; ok {
return errors.New("已经添加过此结点")
}
g.Indexs[nodeName] = len(g.Nodes)
g.Nodes = append(g.Nodes, nodeName)
return nil
}
func (g *MGraph) AddEdge(nodeName1, nodeName2 string, val int) error {
if _, ok := g.Indexs[nodeName1]; !ok {
return errors.New("结点不存在:" + nodeName1)
}
if _, ok := g.Indexs[nodeName2]; !ok {
return errors.New("结点不存在:" + nodeName2)
}
if g.Edges[g.Indexs[nodeName1]][g.Indexs[nodeName2]] != -1 {
return errors.New("边已经存在")
}
g.Edges[g.Indexs[nodeName1]][g.Indexs[nodeName2]] = val
g.Edges[g.Indexs[nodeName2]][g.Indexs[nodeName1]] = val
g.EdgeNum++
return nil
}
func (g *MGraph) GetEdgeList() []Edge {
var edgeList []Edge
for i := 0; i < len(g.Nodes); i++ {
for j := 0; j < i; j++ {
if g.Edges[i][j] >= 0 {
edgeList = append(
edgeList,
Edge{NodeStart: g.Nodes[i], NodeEnd: g.Nodes[j], Val: g.Edges[i][j]},
)
}
}
}
return edgeList
}
func (g *MGraph) GetConnectedEdges(ele string) []Edge {
var edgeList []Edge
k := g.Indexs[ele]
for i := 0; i < len(g.Nodes); i++ {
if g.Edges[k][i] >= 0 {
edgeList = append(
edgeList,
Edge{NodeStart: g.Nodes[k], NodeEnd: g.Nodes[i], Val: g.Edges[k][i]},
)
}
}
return edgeList
}
相关代码的git 连接:
https://gitee.com/gudongkun/datestruct
标签:普利,return,string,04,Edges,斯卡尔,Indexs,Nodes,AddEdge 来源: https://blog.csdn.net/gudongkun1121/article/details/122455469