最短路径(Dijkstra算法)

2018-08-19  本文已影响84人  lkmc2

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

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;
}
运行结果
上一篇 下一篇

猜你喜欢

热点阅读