狄克斯特拉算法---加权图的最短最小问题-有向图

我们知道BFS广度优先算法只能用于查找段数最少的最有路径也就是无权图
如果对于有权图BFS优先算法就不适用了-使用Dijstra算法来解决加权图的最短最有最小的问题

解决的有向正权图对于负权图这个算法不能解决-可以使用------贝尔曼-福德算法(Bellman-Ford algorithm)

我们使用下面的图来介绍算法原理


微信图片_20200117172914.png

用BFS算法我们得到的路径可能是0-1-3或者0-2-3因为是段数最少的
下面介绍Dijstra算法的原理
节点权重图

节点权重图.png

1.从起点开始,找出到的最近的节点(权重最小的)B
2.查找他的邻居节点的权重从B点到A点的最终开销是2+3=5 小于权重图到A点的6,我们就更新6为5
另一个邻居终点2+5=7小于∞更新为7


微信图片_20200117174953.png

3.重复第1和2步骤----->查找节点权重图去除B以外的其他节点的
最后的图变为


微信图片_20200117175305.png

我们发现到达终点的权重最后为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]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容