最短路径
两个顶点之间带权路径长度最短的路径为最短路径。
在带权图当中,把从一个顶点 v 到另一个顶点 u 所经历的边的权值之和称为路径的带权路径之和。
Dijkstra
带权图单源最短路径
步骤
- 初始化数组,并集合 S 初始化为 {0};
- 从顶点集合 V-S 中选出 v j v_j vj ,满足 d i s t [ j ] = M i n { d i s t [ i ] , v i ∈ V − S } dist[j] = Min\{dist[i], v_i \in V-S\} dist[j]=Min{dist[i],vi∈V−S}, v j v_j vj 就是当前求得的最短路径的终点,并另 S ∪ { j } S\cup \{j\} S∪{j}
- 修改此时从 v 0 v_0 v0 出发到集合 V-S 上任意顶点 v k v_k vk 最短路径的长度:若 dist[j] + graph[j] [k] < dist[k] ,则令 dist[k] = dist[j] + graph[j] [k];path[k] = j
- 重复 2,3 操作 n-1 次,直到 S 中包含全部顶点
辅助数组
- s[] : 标记已经计算完成的顶点。数组中的值全部初始化为 0,源点下标的值初始化为1
- dist[] : 记录从源点 v0 到其他各顶点当前的最短路径长度。数组中的值初始化为源点到各个顶点边的权值,即 dist[i]=graph[0] [i]
- path[] : 记录从最短路径中顶点的前驱顶点,即 path[i] 为 v 到 vi 最短路径上 vi 的前驱顶点。数组中的值初始化:若源点 v0 到该顶点 vi 有一条有向边(无向边),则 path[i] = 0;否则 path[i] = -1
以下图为例:
初始化:将dist置为从0到所有节点的距离;s[0]置为1,其余置为0;path除了0以及从0到达不了的节点,其余置为0。
第一轮:找到dist中最小的值【s中下标置为1的不进行比较】,即为下标2对应的值,根据步骤3修改dist与path数组,并且把s[2]置为1
第二轮:找到dist中最小的值【s中下标置为1的不进行比较】,即为下标1对应的值,根据步骤3修改dist与path数组,并且把s[1]置为1
第三轮:找到dist中最小的值【s中下标置为1的不进行比较】,即为下标3对应的值,根据步骤3修改dist与path数组,并且把s[3]置为1
第四轮:找到dist中最小的值【s中下标置为1的不进行比较】,即为下标4对应的值,根据步骤3修改dist与path数组,并且把s[4]置为1
最后得到的结果,根据dist,path就可以得出路径以及长度
代码实现:
C++
void Dijkstra(Graph G, int v){
int s[G.vexnum];
int path[G.vexnum];
int dist[G.vexnum];
for(int i=0;i<G.vexnum;i++){
dist[i]=G.edge[v][i];
s[i] = 0;
if(G.edge[v][i] < MAX)
path[i]=v;
else
path[i]=-1;
}
s[v]=1;
path[v]=-1;
for(i=0;i<G.vexnum; i++){
int min=MAX;
int u;
for(int j=0; j<G.vexnum; j++){
if(s[j]==0 && dist[j]<min){
min=dist[j];
u=j;
}
}
s[u] = 1;
for(int j=0; j<G.vexnum; j++){
if(s[j]==0 && dist[u] + G.Edges[u][j] < dist[j]){
dist[j] = dist[u] + G.Edges[u][j];
path[j]=u
}
}
}
}
Python
def dijkstra(graph, begin=0):
dist = []
s = []
path = []
nums = len(graph)
# 初始化
for i in range(nums):
dist.append(graph[begin][i])
s.append(0)
if graph[begin][i] < MAX:
path.append(begin)
else:
path.append(-1)
s[begin] = 1
path[begin] = -1
for i in range(nums):
min_dist = MAX
min_vex = -1
for j in range(nums):
if s[j] == 0 and dist[j] < min_dist:
min_dist = dist[j]
min_vex = j
s[min_vex] = 1
for k in range(nums):
if s[k] == 0 and dist[min_vex] + graph[min_vex][k] < dist[k]:
dist[k] = dist[min_vex] + graph[min_vex][k]
path[k] = min_vex
return dist, path
if __name__ == '__main__':
graph = [
[0, 5, 3, MAX, 8],
[MAX, 0, 2, 1, MAX],
[MAX, MAX, 0, MAX, 4],
[MAX, MAX, 1, 0, MAX],
[MAX, MAX, MAX, MAX, MAX]
]
dijkstra(graph)
'''
dist : [0, 5, 3, 6, 7]
path : [-1, 0, 0, 1, 2]
'''
算法复杂度 O ( ∣ V ∣ 2 ) O(|V|^2) O(∣V∣2)
Dijkstra 算法并不适用于含有负权边的图
Floyd
各顶点之间的最短路径
算法思想
递推产生一个 n 阶方阵序列 A ( − 1 ) , A ( 0 ) , . . . , A ( k ) , . . . , A ( n − 1 ) A^{(-1)}, A^{(0)}, ...,A^{(k)},...,A^{(n-1)} A(−1),A(0),...,A(k),...,A(n−1)
A ( k ) [ i ] [ j ] A^{(k)}[i][j] A(k)[i][j] 顶点 v i v_i vi 到 v j v_j vj 的最短路径长度,且该路径经过的顶点编号不大于 k
递推公式
初始化: A ( − 1 ) [ i ] [ j ] = g r a p h [ i ] [ j ] A^{(-1)}[i][j] = graph[i][j] A(−1)[i][j]=graph[i][j]
递推方法: A ( k ) [ i ] [ j ] = M i n { A ( k − 1 ) [ i ] [ j ] , A ( k − 1 ) [ i ] [ k ] + A ( k − 1 ) [ k ] [ j ] } , k = 0 , 1 , . . . , n − 1 A^{(k)}[i][j]=Min\{ A^{(k-1)}[i][j], A^{(k-1)}[i][k]+A^{(k-1)}[k][j]\}, k=0,1,...,n-1 A(k)[i][j]=Min{A(k−1)[i][j],A(k−1)[i][k]+A(k−1)[k][j]},k=0,1,...,n−1
代码实现
C++
void Floyd(Graph G){
int A[G.vexnum][G.vexnum];
for(int i=0; i<G.vexnum;i++)
for(int j=0; j<G.vexnum;j++)
A[i][j]=G.Edge[i][j];
for(int k=0;k<G.vexnum;k++)
for(int i=0; i<G.vexnum;i++)
for(int j=0; j<G.vexnum;j++)
if(A[i][j]>A[i][k]+A[k][j])
A[i][j]=A[i][k]+A[k][j];
}
Python
def floyd(graph):
nums = len(graph)
A = []
for i in range(nums):
a = []
for j in range(nums):
a.append(graph[i][j])
A.append(a)
print(A)
for i in range(nums):
for j in range(nums):
for k in range(nums):
if A[i][j] > A[i][k] + A[k][j]:
A[i][j] = A[i][k] + A[k][j]
return A
以上图为例
if __name__ == '__main__':
graph = [
[0, 5, 3, MAX, 8],
[MAX, 0, 2, 1, MAX],
[MAX, MAX, 0, MAX, 4],
[MAX, MAX, 1, 0, MAX],
[MAX, MAX, MAX, MAX, MAX]
]
# dijkstra(graph)
A = floyd(graph)
print(A)
'''
[[0, 5, 3, 6, 7],
[100, 0, 2, 1, 6],
[100, 100, 0, 100, 4],
[100, 100, 1, 0, 5],
[100, 100, 100, 100, 100]]
'''
时间复杂度 O ( ∣ V ∣ 3 ) O(|V|^3) O(∣V∣3)