图及常见算法

图是一种非常重要且应用广泛的数据结构。

一个图主要由多个顶点及连接顶点的边组成。

其中根据图中的边是否包含方向分为有向图和无向图。

根据图中的边是否包含权值分为有权图和无权图。

图的表示方式

图的表示方式分为两种:一种是邻接矩阵,经常用来表示稠密图,第二种是邻接表,常用来表示稀疏图。

图的遍历

图的遍历分为广度优先搜索(BFS)和深度优先搜索(DFS),两种都可以完成图的遍历。

广度优先搜索:用队列来实现

深度优先搜索:可以通过递归或者栈来实现

最短路径

多源最短路径:弗洛伊德(Floyd)算法

核心思想:使用邻接矩阵adj来存储各顶点的最短距离。使用二维数据来模拟邻接矩阵,例如adj[1][1]=0表示顶点1到顶点1的距离为0,同理adj[1][2]表示顶点1到顶点2的距离。先对邻接矩阵进行初始化,初始化时,如果顶点i和顶点j直接相连,初始化时,如果两个顶点直接相连,两点之间的距离初始化为该边的距离,否则初始化为一个无穷大的值。然后判断在只允许经过顶点1中转的情况下,依此遍历每个顶点到其他顶点的距离,判断其距离是否会因为经过顶点1而使两个顶点之间的距离变短,如果距离变短则更新顶点之间的距离;继续加入允许经过的顶点2,使经过1和2顶点的情况下,两个顶点的距离是否变短,遍历n次,使允许经过所有顶点。得到各顶点之间的最短路径。

func Floyd() {
   n := 4            // 输入4个顶点
   var adj [4][4]int // 二维数组表示邻接矩阵,值为两顶点的距离
   // 初始化
   for i := 0; i < n; i++ {
      for j := 0; j < n; j++ {
         if i == j {
            adj[i][j] = 0
         } else {
            adj[i][j] = 9999 // 用9999来表示一个不可能达到的无穷大的值
         }
      }
   }

   // 输入初始的距离
   adj[0][1] = 3
   adj[0][3] = 2
   adj[1][2] = 6
   adj[1][3] = 5
   adj[2][3] = 4
   adj[3][0] = 3
   adj[3][1] = 4
   adj[3][2] = 4

   // 核心代码
   for k := 0; k < n; k++ {
      for i := 0; i < n; i++ {
         for j := 0; j < n; j++ {
            if adj[i][k]+adj[k][j] > adj[i][j] {
               adj[i][j] = adj[i][k] + adj[k][j]
            }
         }
      }
   }
}

关键字:依此允许经过顶点使顶点距离变短、三重遍历、动态规划。时间复杂度为n^3。

单源最短路径:迪杰斯特拉(Dijkstra)算法

核心思想:用一个一维数组Dis表示单源顶点到其他顶点的距离,初始化时,如果顶点i与源点直接相连,那么Dis[i]等于该条边的距离,否则Dis[i]等于无穷大表示不相连。然后在除源点以外的其他顶点中找到离源点最近的点,将该顶点标记为确定顶点,该距离为已确定距离,然后以该点,对该点相邻的边进行松弛,松弛之后再找离源点最近的点标记为已确定,依此类推,知道所有的点都被确认。使用邻接表还可以降低算法的复杂度。

func Dijkstra() {
   // 输入,确定有5个顶点,分别是0,1,2,3,4,查找以0为源点的单源最短路径。
   // 有以下5条边(无向)
   //input := [][3]int{
   // [3]int{0, 2, 5}, // 表示0到2距离5
   // [3]int{1, 2, 3},
   // [3]int{1, 4, 4},
   // [3]int{2, 3, 3},
   // [3]int{3, 4, 6},
   //}
   // 邻接表存储图
   edges := []map[int]int{
      {2: 5}, //表示0号顶点到2号顶点距离是5
      {2: 3, 4: 4},
      {0: 5, 1: 3, 3: 3},
      {2: 3, 4: 6},
      {1: 4, 3: 6},
   }
   // 算法部分
   dis := make([]int, 5, 5)
   dis[0] = 0
   for i := 1; i < 5; i++ {
      dis[i] = 9999
   }
   // 初始化0号顶点到邻接顶点的距离
   for i, d := range edges[0] {
      dis[i] = d
   }
   fmt.Println(dis)
   books := new([5]bool)
   for i := 0; i < 5; i++ {
      var min = 9999
      var minI = -1
      for k, v := range dis {
         if books[k] == false && v < min {
            min = v
            minI = k
         }
      }
      if minI < 0 {
         return
      }
      books[minI] = true
      for k, v := range edges[minI] {
         if dis[k] > min+v {
            dis[k] = min + v
         }
      }
   }
   fmt.Println(dis)
}

关键字:已确认、松弛

负权边最短路径:贝尔曼(Bellman)算法

Bellman算法还是用一维数组来表示顶点到其他顶点的距离。初始化时所有顶点到源点的距离都是无穷大。然后每轮遍历所有的边,判断能否通过这条边使源点到边后面顶点的距离变短。需经过至多n-1轮遍历就可以找到最短路径。

关键字:至多n-1轮

关于图的更多相关知识https://blog.youkuaiyun.com/saltriver/article/details/54428685 写的比较清楚了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值