前言
书接上回,我们写了个无向图的广度优先遍历的代码
用邻接矩阵表示无向图,并做广度优先遍历(golang)-CSDN博客文章浏览阅读557次,点赞8次,收藏8次。上学没好好学,只能问问度娘,然后度娘给出的答案全是文字。。。我在这里画个图描述一下吧。简单点来说就是用二维数组来表达 一个图 上 各个点之间的连接情况,这里我们用无向图来举例可以看到图中有ABCDEF六个点,我们先忽略其中的连线,假设六个点互不相连,那么我们就用如下二维数组来表示0就代表两个点之间没有连线,本文不考虑A点和A点自己连线这种情况先。https://blog.csdn.net/woaixuejinglzq/article/details/142831038?spm=1001.2014.3001.5502然后今天,去写一下Prim算法,我们用的图结构,还是上面文章的,继续用邻接矩阵来表达图,但是对于不存在的线,前文用0来表达,这里我们使用mat.MaxInt,具体为啥,我会在代码注释中表达呢
package main
type Graph struct {
dots []string // 点
matrix [][]int // 邻接矩阵
}
最小生成树
什么是生成树?
图论:一个连通无向图的生成树是通过移除一些边得到的树形结构,它包含图中的所有顶点,但没有回路。
什么是最小生成树?
是指在所有可能的生成树中,边的权值和最小的那棵树
注意点:
对于有n个点的连通图来说,它的生成树有n-1条边,大于n-1必然有回路,小于n - 1 必然不连通
Prim算法
原理
将图中的点分为两类,一类是已经遍历的点(A类),一类是未被遍历的点(B类),我们每一次都找和A类中的点 相连的B类点中 边权值最小的点,然后将该点加入A类,直到所有点都被遍历一次,每一次都找新的点必然不会有回路出现,每一次找权值最小的线,必然是总权值最小
画图解释
先画一个连通图,我们从A点开始遍历
1、与A相邻的点有B、C、F三个点,其中最小的是AB线段,AB线段成为树中的一条边
2、和A、B两点相连的所有点中 F、C、D,最短的是 AC、AF,我们取哪个都可以的,因为如果AF比后面的线短,早晚还得加进最小生成树,取AC先
3、和ABC三点相连的所有点中F、D、E,权重最小的是AF
4、下一个是CD
5、下一个是DE
至此,最小生成树完成如下
代码
package main
import (
"container/list"
"fmt"
"math"
)
type Graph struct {
dots []string // 点
matrix [][]int // 邻接矩阵
}
func InitGraph(graph *Graph) error {
dotsNum := len(graph.dots)
if dotsNum == 0 {
return fmt.Errorf("no dots")
}
// 建立矩阵,初始值都为0,意为,现在各个点互相不连接
graph.matrix = make([][]int, dotsNum)
for i := 0; i <= dotsNum-1; i++ {
graph.matrix[i] = make([]int, dotsNum)
for j := 0; j <= dotsNum-1; j++ {
graph.matrix[i][j] = math.MaxInt
}
}
// AB
graph.matrix[0][1] = 1
graph.matrix[1][0] = 1
// BD
graph.matrix[1][3] = 5
graph.matrix[3][1] = 5
// AC
graph.matrix[0][2] = 2
graph.matrix[2][0] = 2
// AF
graph.matrix[0][5] = 2
graph.matrix[5][0] = 2
// CD
graph.matrix[2][3] = 3
graph.matrix[3][2] = 3
// CE
graph.matrix[2][4] = 4
graph.matrix[4][2] = 4
// CF
graph.matrix[2][5] = 5
graph.matrix[5][2] = 5
// EF
graph.matrix[4][5] = 6
graph.matrix[5][4] = 6
// DE
graph.matrix[3][4] = 3
graph.matrix[4][3] = 3
return nil
}
func ShowGraph(graph *Graph) {
for i := 0; i <= len(graph.dots)-1; i++ {
fmt.Println(graph.matrix[i])
}
}
func prim(graph *Graph) {
// 总点数
dotCount := len(graph.dots)
// graph.dots中的下标
startPointIndex := 0
// 最小生成树总权重
sumWeight := 0
// 最小生成树线集合
lines := []string{}
// 值:代表当前已遍历点 所连接 未被遍历点 的线 的权重,
// 数组下标:就代表着graph.dots中的下标,代表着未被访问且与已被相连的点的下标 和 不相连的点(weightRecord中为0)
weightRecord := make([]int, dotCount)
// 值:已经被遍历的点,数组的值就是graph.dots中的下标,代表着已经被访问到的点的下标
// 下标:数组的下标也代表 graph.dots中的下标,代表着未被访问且与已被相连的点的下标 和 不相连的点(visitList中为-1)
visitList := make([]int, dotCount)
//先从下标为0的开始,这里也就是A点
for i := 0; i <= dotCount-1; i++ {
// 将所有与A相连的点赋值,如果不相连就是 -1
weightRecord[i] = graph.matrix[startPointIndex][i]
// 全是0,代表A点
visitList[i] = 0
}
weightRecord[0] = -1
// 每个点都遍历一次,A已经被遍历过了,所以做 dotCount - 1次循环
// 如果要走到dotCount次,weightRecord都变成-1 结果包是 *A,多一条线会
for i := 1; i <= dotCount-1; i++ {
minWeightNow := math.MaxInt
nextDotIndex := 0
for j := 0; j <= dotCount-1; j++ {
// weightRecord为0 代表着没有连线
if weightRecord[j] != -1 && weightRecord[j] < minWeightNow {
minWeightNow = weightRecord[j]
nextDotIndex = j
}
}
sumWeight += weightRecord[nextDotIndex]
lines = append(lines, graph.dots[visitList[nextDotIndex]]+graph.dots[nextDotIndex])
// fmt.Println(nextDotIndex)
weightRecord[nextDotIndex] = -1
// 更新weightRecord 和 visitList
for j := 0; j < dotCount; j++ {
// 这里解释一下
// 我们假设与nextDotIndex相连的已被遍历的节点为A
// 假设nextDotIndex为B点
// 假设与nextDotIndex相连未被遍历的节点为C
// 这里有三种情况
// 1、A和C不相连,那么好说,直接越过就好了,本身不相连的点在矩阵中也是math.MaxInt,也不需要多余判断,
// 我们就不改weightRecord 和 visitList
// 2、如果A和C相连,且AC权重大于BC,那么我们舍弃AC就好了,记录下来BC,更改weightRecord 和 visitList
// 这里我们将visitList中的C的下标的值 改为 B的下标值(dots数组的下标)
// 3、如果A和B相连,且AC权重小于BC,我们就不改weightRecord 和 visitList
if weightRecord[j] != -1 && graph.matrix[nextDotIndex][j] < weightRecord[j] {
weightRecord[j] = graph.matrix[nextDotIndex][j]
visitList[j] = nextDotIndex
}
}
// fmt.Println(weightRecord)
// fmt.Println(visitList)
// break
}
fmt.Println("sum:", sumWeight)
fmt.Println(lines)
}
func main() {
graph := &Graph{}
graph.dots = []string{"A", "B", "C", "D", "E", "F"}
InitGraph(graph)
ShowGraph(graph)
prim(graph)
}
最后
小弟太菜,写的可能又臭又长,还求大佬们多多指导QAQ