最短路径问题 —— Dijkstra 和 Floyd 算法

最短路径

两个顶点之间带权路径长度最短的路径为最短路径。

在带权图当中,把从一个顶点 v 到另一个顶点 u 所经历的边的权值之和称为路径的带权路径之和。

Dijkstra

带权图单源最短路径

步骤

  1. 初始化数组,并集合 S 初始化为 {0};
  2. 从顶点集合 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],viVS} v j v_j vj 就是当前求得的最短路径的终点,并另 S ∪ { j } S\cup \{j\} S{ j}
  3. 修改此时从 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
  4. 重复 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(V2)

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(n1)

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(k1)[i][j],A(k1)[i][k]+A(k1)[k][j]},k=0,1,...,n1

代码实现

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(V3)

猜你喜欢

转载自blog.csdn.net/m0_38007695/article/details/110392328