图
图是一种非常重要且应用广泛的数据结构。
一个图主要由多个顶点及连接顶点的边组成。
其中根据图中的边是否包含方向分为有向图和无向图。
根据图中的边是否包含权值分为有权图和无权图。
图的表示方式
图的表示方式分为两种:一种是邻接矩阵,经常用来表示稠密图,第二种是邻接表,常用来表示稀疏图。
图的遍历
图的遍历分为广度优先搜索(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 写的比较清楚了