美文网首页
最短路径(Dijkstra算法)

最短路径(Dijkstra算法)

作者: lkmc2 | 来源:发表于2018-08-19 17:15 被阅读84次

    最短路径:两顶点之间经过的边上权值之和最小的路径。

    Dijkstra算法:使用两个数组,数组P记录到对应顶点的前驱节点的下标,数组D记录起点v0到各顶点的最小路径权值之和; 之后开始循环,从v0开始,找到跟v0路径最短的顶点w1,找到与顶点w1相连的顶点,更新这些顶点到v0的最短距离,;下一次的循环从w1开始,找到离w1距离最近的顶点w2,找到与顶点w2相连的顶点,更新这些顶点到v0的最短距离……直到遍历完所有顶点。

    实现代码如下:

    // 最短路径(Dijkstra算法)
    #include <stdio.h>
    
    #define MAXVEX 20 // 最大顶点数
    #define INFINITY 65535 // 无穷
    
    typedef int Patharc[MAXVEX]; // 用于存储最短路径前驱结点下标的数组
    typedef int ShortPathTable[MAXVEX]; // 用于存储到各点最短路径的权值和
    
    // 邻接矩阵结构
    typedef struct {
        int vexs[MAXVEX]; // 顶点表
        int arc[MAXVEX][MAXVEX]; // 边表
        int numNodes, numEdges; // 图中当前的顶点数,边数
    } MGraph;
    
    /**
     * 生成邻接矩阵(图)
     * @param G 邻接矩阵(图)
     */
    void CreateMGraph(MGraph *G) {
        int i, j; // 用于遍历元素
    
        G->numEdges = 16; // 设置有16条边
        G->numNodes = 9; // 设置有9个顶点
    
        // 初始化图的顶点
        for (i = 0; i < G->numNodes; i++) {
            G->vexs[i] = i;
        }
    
        // 初始化图的边
        for (i = 0; i < G->numNodes; i++) {
            for (j = 0; j < G->numNodes; j++) {
                if (i == j) { // 对角线边的值设置为0
                    G->arc[i][j] = 0;
                } else { // 其他位置的边设置为无穷
                    G->arc[i][j] = G->arc[j][i] = INFINITY;
                }
            }
        }
    
        // 设置特定边的权
        G->arc[0][1]=1;
        G->arc[0][2]=5;
    
        G->arc[1][2]=3;
        G->arc[1][3]=7;
        G->arc[1][4]=5;
    
        G->arc[2][4]=1;
        G->arc[2][5]=7;
    
        G->arc[3][4]=2;
        G->arc[3][6]=3;
    
        G->arc[4][5]=3;
        G->arc[4][6]=6;
        G->arc[4][7]=9;
    
        G->arc[5][7]=5;
    
        G->arc[6][7]=2;
        G->arc[6][8]=7;
    
        G->arc[7][8]=4;
    
        // 设置对称边的权
        for (i = 0; i < G->numNodes; i++) {
            for (j = i; j < G->numNodes; j++) {
                G->arc[j][i] = G->arc[i][j];
            }
        }
    }
    
    /**
     * 最短路径Dijkstra算法
     * 求有向网G的v0顶点到其余顶点v的最短路径P[v]即带权长度D[v]
     * P[v]的值为前驱顶点下标,D[v]表示v0到v的最短路径长度和
     * @param G 邻接矩阵(图)
     * @param v0 图的起始顶点
     * @param P 存储最短路径前驱节点下标的数组
     * @param D 存储v0到各点最短路径的权值和的数组
     */
    void ShortestPath_Dijkstra(MGraph G, int v0, Patharc *P, ShortPathTable *D) {
        int v, w, k, min; // v、w用来遍历元素,k记录最小权值节点的下标,min记录最小权值
        int final[MAXVEX]; // final[w]=1表示v0至vw的最短路径
    
        // 初始化数据
        for (v = 0; v < G.numNodes; v++) {
            final[v] = 0; // 全部顶点初始化为位置最短路径状态
            (*D)[v] = G.arc[v0][v]; // 记录与v0有连线的顶点的权值
            (*P)[v] = -1; // 初始化路径数组P为-1
        }
    
        (*D)[v0] = 0; // 设置v0至v0路径的权值为0
        final[v0] = 1; // v0至v0不需要求路径,设置为1
    
        // 开始主循环,每次求得v0到某个v顶点的最短路径
        for (v = 1; v < G.numNodes; v++) {
            min = INFINITY; // 记录本次循环距离v0最近的距离
    
            // 寻找离v0最近的顶点
            for (w = 0; w < G.numNodes; w++) {
                // final[w]为0表示该顶点还没有记录与它最近的顶点
                // final[w]为0,并且该顶点的权值小于无穷
                if (!final[w] && (*D)[w] < min) {
                    k = w; // 记录最小权值的下标
                    min = (*D)[w]; // 记录最小权值
                }
            }
    
            // 将目前找到的最接近v0的顶点的下标的位置置为1,表示该顶点已被记录
            final[k] = 1;
    
            // 修正当前最短路径即距离
            for (w = 0; w < G.numNodes; w++) {
                // 如果经过v顶点的路径比现在这条路径短的话
                if (!final[w] && (min + G.arc[k][w] < (*D)[w])) {
                    (*D)[w] = min + G.arc[k][w]; // 修改顶点w距离v0的最短长度
                    (*P)[w] = k; // 存储最短路径前驱节点的下标
                }
            }
        }
    }
    
    /**
     * 打印最短路径
     * @param G 邻接矩阵(图)
     * @param v0 图的起始顶点
     * @param P 存储最短路径前驱节点下标的数组
     * @param D 存储v0到各点最短路径的权值和的数组
     */
    void PrintShortPath(MGraph G, int v0, Patharc P, ShortPathTable D) {
        int i, j; // 用于遍历元素
    
        printf("存储最短路径前驱结点下标的数组P的值为:\n");
        printf("数组下标:");
        for (i = 0; i < G.numNodes; i++) {
            printf("%2d ", i);
        }
        printf("\n数组的值:");
        for (i = 0; i < G.numNodes; i++) {
            printf("%2d ", P[i]);
        }
        
        /*
            存储最短路径前驱结点下标的数组P的值为:
            数组下标: 0  1  2  3  4  5  6  7  8
            数组的值:-1 -1  1  4  2  4  3  6  7
            
            当P[8] = 7时表示,顶点8的前驱结点是顶点7,
            找到顶点7,发现P[7] = 6,表示顶点7的前驱节点是顶点6,
            所以由顶点8到顶点0的最短路径为:
            8 -> 7 -> 6 -> 3 -> 4 ->2 -> 1
            将这个顺序倒过来即可得到顶点0到顶点8的最短路径
         */
    
        printf("\n\n最短路径倒序如下:\n");
        for (i = 1; i < G.numNodes; i++) {
            printf("v%d - v%d : ", v0, i);
            j = i; // j用于遍历while循环
    
            while (P[j] != -1) { // P[j] = 1表示该顶点不可通
                printf("%d ", P[j]);
                j = P[j]; // 获取与该顶点距离最近的顶点下标
            }
            printf("\n");
        }
    
        printf("\n原点v%d到各顶点的最短路径长度为:\n", v0);
        for (i = 1; i < G.numNodes; i++) {
            // D数组中存v0到各顶点的最短路径和
            printf("v%d - v%d : %d \n", G.vexs[0], G.vexs[i], D[i]);
        }
    }
    
    int main() {
        int v0; // 图的起始顶点下标
        MGraph G; // 邻接矩阵(图)
        Patharc P; // 用于存储最短路径前驱结点下标的数组
        ShortPathTable D; // 用于存储到各点最短路径的权值和
    
        v0 = 0; // 将0下标的顶点设置为图的起点
    
        CreateMGraph(&G); // 初始化邻接矩阵(图)
        ShortestPath_Dijkstra(G, v0, &P, &D); // 对图求最短路径
        PrintShortPath(G, v0, P, D); // 打印最短路径
    
        return 0;
    }
    
    运行结果

    相关文章

      网友评论

          本文标题:最短路径(Dijkstra算法)

          本文链接:https://www.haomeiwen.com/subject/tyiciftx.html