Day 59 - 图论 Part 09

17 阅读2分钟

基础

刷题

dijkstra 算法 (堆优化)

图的存储

关于图的存储主要有两种:邻接矩阵和 和 邻接表

邻接矩阵

image.png

邻接矩阵的优点:

  • 表达方式简单,易于理解
  • 检查任意两个顶点间是否存在边的操作非常快
  • 适合稠密图,在边数接近顶点数平方的图中,邻接矩阵是一种空间效率较高的表示方法。

缺点:

  • 遇到稀疏图,会导致申请过大的二维数组造成空间浪费 且遍历 边 的时候需要遍历整个n * n矩阵,造成时间浪费

邻接表

image.png

邻接表的优点:

  • 对于稀疏图的存储,只需要存储边,空间利用率高
  • 遍历节点链接情况相对容易

缺点:

  • 检查任意两个节点间是否存在边,效率相对低,需要 O(V)时间,V表示某节点链接其他节点的数量。
  • 实现相对复杂,不易理解

堆排序

container/heap package

package main
import (
    "container/heap"
    "fmt"
)

type IntHeap []int

func (h IntHeap) Len() int           { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] } // 最小堆
func (h IntHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

func (h *IntHeap) Push(x interface{}) {
    *h = append(*h, x.(int))
}

func (h *IntHeap) Pop() interface{} {
    old := *h
    n := len(old)
    x := old[n-1]
    *h = old[0 : n-1]
    return x
}

func main() {
    h := &IntHeap{2, 1, 5}
    heap.Init(h)
    heap.Push(h, 3)
    fmt.Printf("最小值: %d\n", (*h))
    for h.Len() > 0 {
        fmt.Printf("%d ", heap.Pop(h))
    }
    // 输出: 最小值: 1 1 2 3 5
}


image.png

  1. 城市间货物运输 I

kamacoder.com/problempage…

image.png

加了updated,来判断是否 所有的边都会影响minDist里面的数组,如果没有影响,可以直接跳出,优化程序。但是还是会超时。

具体思路:

Bellman_ford 算法

Bellman_ford算法的核心思想是 对所有边进行松弛n-1次操作(n为节点数量),从而求得目标最短路

当使用前面的思路,例如prim,dijkstra算法,你无法使用最短的那个路径,因为图的边权值是存在负数的,当你判定这条边是最短的,有可能会存在多个边加在一起会更短,因为有负数权值的边。

因为会存在环路,不能按照节点的考虑,只能按照边来考虑,故 使用 Bellman_ford算法,对所有的边 松弛 n-1次。

这里的 松弛 是指多个边指向一个节点, 当遍历到 c -> b,if (value1 < value) { minDist[B] = value1}, 找到最合适的边的权值(这里求的是最小值)

image.png

为什么是n-1次?

首先n是节点的数量,n-1次是理论上需要的最大次数。因为当遍历一个边,出发节点如果没有被计算,就无法计算这个边的,所以最差的情况是,每一次只能遍历一条边,而从1到n,最长需要有n-1个边(不考虑环)

总结

dijkstra算法堆优化算法需要再做一遍