我们知道BFS广度优先算法只能用于查找段数最少的最有路径也就是无权图
如果对于有权图BFS优先算法就不适用了-使用Dijstra算法来解决加权图的最短最有最小的问题
解决的有向正权图对于负权图这个算法不能解决-可以使用------贝尔曼-福德算法(Bellman-Ford algorithm)
我们使用下面的图来介绍算法原理
用BFS算法我们得到的路径可能是0-1-3或者0-2-3因为是段数最少的
下面介绍Dijstra算法的原理
节点权重图
1.从起点开始,找出到的最近的节点(权重最小的)B
2.查找他的邻居节点的权重从B点到A点的最终开销是2+3=5 小于权重图到A点的6,我们就更新6为5
另一个邻居终点2+5=7小于∞更新为7
3.重复第1和2步骤----->查找节点权重图去除B以外的其他节点的
最后的图变为
我们发现到达终点的权重最后为6
也就是从起点->B->A->终点
下面简化一下步骤
1.找出最便宜的节点,也就是最短时间到达的节点
2.查找该节点的邻居,查找是不是有前往邻居的更近的节点,有就更新节点
3.重复上面的两个步骤,遍历所有的节点
4.计算最终的路径
算法实现
1.需要一个有向图结构
2.除去起点的所有节点的权重图的散列表
3.需要一个除去起点的所有节点的父节点的散列表
数据结构也是可以优化的,不做讨论
有向带权图的初始化
package main
import (
"fmt"
"math"
"strconv"
)
// 逻辑不是很严谨 越界的没考虑-- scanf
// 边节点结构
type EdgeTableNode struct {
index int // 顶点索引
weight int // 权重
edgeTableNode *EdgeTableNode // 下一个临界点的指针
}
// 顶点的数据信息
type VertInfo struct {
value int
name string
}
// 顶点节点
type VertNode struct {
vertInfo VertInfo // 定点的数据信息
edgeTableNode *EdgeTableNode
}
type Graph struct {
vertNum int
grapType uint8
edgeNum int
hasCircle bool
allCircle [][]int
visted []int
vertNode []*VertNode
}
var arrayList []int
func NewGraph(vertNum int, edgeNum int, grapType uint8) *Graph {
return &Graph{vertNum: vertNum, hasCircle: false, allCircle: [][]int{},
visted: make([]int, vertNum), grapType: grapType,
edgeNum: edgeNum, vertNode: []*VertNode{}}
}
// 只做了有向图的初始化
// 没考虑无向图
func (this *Graph) InitGrap() {
// 顶点初始化
for i := 0; i < this.vertNum; i++ {
vert := &VertNode{}
vert.vertInfo.value = i
vert.vertInfo.name = "V" + strconv.Itoa(i)
fmt.Println(*vert)
this.vertNode = append(this.vertNode, vert)
}
// 边初始化
var startVert int
var endVert int
var weight int
var n int
for i := 0; i < this.edgeNum; i++ {
n, _ = fmt.Scanf("%d %d %d", &startVert, &endVert, &weight)
fmt.Printf("%d %d %d\n", startVert, endVert, n)
var edgeNode = this.vertNode[startVert].edgeTableNode
if edgeNode == nil {
tempNode := new(EdgeTableNode)
tempNode.weight = weight
tempNode.index = endVert
tempNode.edgeTableNode = nil
this.vertNode[startVert].edgeTableNode = tempNode
continue
}
for edgeNode != nil {
// 单链表尾插节点
if edgeNode.edgeTableNode == nil {
tempNode := new(EdgeTableNode)
tempNode.weight = weight
tempNode.index = endVert
tempNode.edgeTableNode = nil
edgeNode.edgeTableNode = tempNode
break
}
edgeNode = edgeNode.edgeTableNode
}
}
}
算法的核心代码
// 获取权重最低的节点
// 不能包含已经搜索过的
func (this *Graph) MinCostsNode(costs map[int]float64) int {
minCostIndex := -1
inf := math.Inf(1)
for key, value := range costs {
// 已经被查找过
if this.visted[key] == 1 {
continue
}
if value < inf {
minCostIndex = key
inf = value
}
}
return minCostIndex
}
func (this *Graph) Dijkstra(start, end int) []int {
if start >= this.vertNum || start < 0 || end >= this.vertNum || end < 0 {
return nil
}
// 除起点外所有要到达的节点的对应权重
mapCosts := make(map[int]float64)
// 从哪个节点到这个节点的映射关系
// key值与上面的mapCosts一样
mapParent := make(map[int]float64)
// 匿名函数初始化
func() {
//
for i := 0; i < this.vertNum; i++ {
if i == start {
node := this.vertNode[i]
edgeNode := node.edgeTableNode
for edgeNode != nil {
mapCosts[edgeNode.index] = float64(edgeNode.weight)
mapParent[edgeNode.index] = float64(i)
edgeNode = edgeNode.edgeTableNode
}
continue
}
// 其他非起点的邻居节点设置为∞
if _, ok := mapCosts[i]; !ok {
mapCosts[i] = math.Inf(0)
mapParent[i] = math.Inf(0)
}
}
}()
minCostIndex := this.MinCostsNode(mapCosts)
for minCostIndex != -1 {
// 获取他的邻接点
node := this.vertNode[minCostIndex]
// 获取他的所有邻接点
edgeNode := node.edgeTableNode
for edgeNode != nil {
// 如果到达邻接点的权重+最小权重的值小于邻接点的权重就更新 起点到达邻接点的权重
tempCost := float64(edgeNode.weight) + mapCosts[minCostIndex]
if tempCost < mapCosts[edgeNode.index] {
mapCosts[edgeNode.index] = tempCost
// 更新父节点
mapParent[edgeNode.index] = float64(minCostIndex)
}
// 查找剩余的邻接点
edgeNode = edgeNode.edgeTableNode
}
// 当前节点已经处理过了
this.visted[minCostIndex] = 1
minCostIndex = this.MinCostsNode(mapCosts)
}
var path []int
// 逆序遍历出路径
if math.IsInf(mapParent[end], 0) {
return path
}
// 查找路径
path = append(path, end)
value := int(mapParent[end])
for value != start {
path = append(path, value)
value = int(mapParent[value])
}
path = append(path, start)
// 是逆序的寻路点
// 可以倒序得到顺序的节点
fmt.Println(path)
return path
}
main函数测试代码
测试的图用的是上面的第一幅图来测试
func GrapDijstra() {
var grap = NewGraph(4, 5, 1)
grap.InitGrap()
grap.Dijkstra(0, 3)
}
func main() {
//GrapDfs()
//GrapTuoPuSort()
//GrapBfs()
GrapDijstra()
}
输入奇数行是输入-偶数行书打印的日志
0 1 6
0 1 3
0 2 2
0 2 3
1 3 1
1 3 3
2 1 3
2 1 3
2 3 5
2 3 3
结果
[3 1 2 0]