Skip to content

最短路径问题

定义

寻找一个带权有向图中某一点到另一个点的最短路径/权最小的路径

当然以下算法也适用于无向图

Floyd (弗洛伊德) 算法

  • 可以算出所有点之间的最短距离
// dist 初始化为邻接矩阵,若i没有指向j的边,则应有 dist[i][j]=MAX_INT(表示无穷大的数)
void floyd(){
    for(int k = 0; k < n; k ++){   //作为循环中间点的k必须放在最外一层循环
        for(int i = 0; i < n; i ++){
            for(int j = 0; j < n; j ++){
                dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
            }
        }
    }
}
//dist[i][j]得出的是i到j的最短路径
  • 时间复杂度为:\(O(n^{3})\)

Warshall 算法

和 Floyd(弗洛伊德)算法很像,二者常常一起统称为 Warshall-Floyd 算法 ,三重循环一样,只是循环最内层执行的赋值不一样

  • 用于由邻接矩阵计算可达矩阵

  • 由关系矩阵计算传递闭包矩阵

  • 关系矩阵即为邻接矩阵,对应的传递闭包矩阵即为可达矩阵

// dist 初始化为邻接矩阵,若存在i指向j的边,则应有 dist[i][j]=1
void floyd(){
    for(int k = 0; k < n; k ++){   //作为循环中间点的k必须放在最外一层循环
        for(int i = 0; i < n; i ++){
            for(int j = 0; j < n; j ++){
                dist[i][j] = dist[i][j] | (dist[i][k] && dist[k][j]);
            }
        }
    }
}
//dist[i][j]得出的可达矩阵

或者:

void floyd(){
    for(int i = 0;i < n;i++){  //先列
        for(int j = 0;j < n;j++){  //后行
            if(matrix[j][i] ** 1){  //  第j行与第i行进行或操作,并最终赋值到第j行
                for(int k = 0;k < n;k++){
                    matrix[j][k] = matrix[j][k] | matrix[i][k];
                }
            }
        }
    }
}

Info

初始化邻接矩阵对角线元素\(d[i][i]\)为 0,得到的可达矩阵若\(d[i][i]=1\)则存在包含点 i 的环,从而可以判断图是否有环和找环 - 时间复杂度为:\(O(n^{3})\)

Dijkstra (迪杰斯特拉) 算法

可以算出某一个点到其他所有点之间的最短距离,即单源点最短路径

对 Prim 算法略加改动就成了 Dijkstra 算法, 将 Prim 算法中求每个顶点\(V_k\)的 lowcost 值用 dist[k]代替即可。

与 Prim 算法一样用了贪心思想,只有在每条边权值非负的情况下才能保证结果正确

  • dist[i] 是点 s 出发到 i 的最短路径长度,长度为 n
  • 初始需要 dist[i]赋值为 s 出发的边指向的各个点的权
  • 剩余不相邻的点全部为 MAX_INT(表示无穷大的数)
  • shortest[i] 是标记 s 到点 i 的距离 dist[i]是否确定为最短路径,长度为 n
void dijkstra(int s){
    memset(shortest, false, sizeof(shortest));
    shortest[s] = true;
    for(int i = 0; i < n; i ++){
        dist[i] = graph[s][i]; //graph[s][i]为s到i的有向边的权
        //初始化dist[i]赋值为s出发的边指向的各个点的权
    }
    int index;
    for(int i = 1; i < n; i ++){ //一共n-1轮后s到其余所有点到距离都是最短距离
        int mincost = MAX_INT; //表示无穷大的数
        for(int j = 0; j < n; j ++){
            if(!shortest[j] && dist[j] < mincost){
                mincost = dist[j];
                index = j;
            }
        } //此处可以改用堆,存储dist中未被确定为最短距离的元素和下标,维护最小值
        shortest[index] = true; //第i轮确定i到index的距离为最短距离
        for(int j = 0; j < n; j ++){
            if(!shortest[j] && dist[j] > dist[index] + graph[index][j]){
                dist[j] = dist[index] + graph[index][j];
            }
        }
    }
}
  • 时间复杂度为:\(O(n^{2})\)