最短路径Dijkstra算法

最短路径问题:
从图的一个顶点出发,到达图的另一个顶点的最短路径

解法:
迪杰斯特拉算法(Dijkstra算法)
弗洛伊德算法(Floyd算法)
SPFA算法

Dijkstra算法介绍

主要是通过广度优先遍历,对每个点进行松弛操作找到源点到其他每个点的最短路径

具体实现流程:
一、初始化图
1、首先定义图的结构体包含邻接数组和顶点数组
2、然后输入每一个顶点的值,然后按输入顶点顺序来输入邻接数组的每个值
3、为了方便自己到自己可以设置为0,没有路径设置为-1,有路径设置路径值
4、对邻接数组进行加工,把距离为-1的设置为默认的无穷大
二、通过Dijkstra求最短路径

思想:通过一个距离数组不断松弛求最短路径,松弛过程中记录通过的点
第一次将源点的数据记录到距离数组并将源点标记,然后依次找最近的未标记的点进行松弛操作,所有的点标记后得到的数组就是到其他每个点的最短距离,可以通过前驱节点来找到对应的路径。

松弛操作:比如1 2 3 4四个顶点求1到4的最短路径,首先加入1,通过邻接矩阵得到1到其他每个点的最短路径,然后加入离1最近的点(比如3),松弛 其他距离(判断1到2的直接距离小还是通过3到2 的间接距离小)(判断1到4的直接距离小还是通过3到4的间接距离小),依次对其他每个未标记的点进行判断取最小

5、首先定义需要的数组,包含used判断每个顶点是否已经被标记
6、通过距离数组记录源点到其他点的距离
7、前驱节点数组用来记录当前节点离源点最近的路径的上一个点,用来寻找最短路径
8、初始化数组,标记数组都设置为未标记,源点设置为已标记,距离数组读取邻接矩阵中源点的行,前驱节点都设置为自己
9、从源点开始找最近的点一次进行松弛操作,找到最近且未标记的点,判断到其他未标记的点是不是可以通过这个新加入的点所短路径,如果可以缩短就修改距离和前驱阶段,直到所有顶点都被标记
10、通过前驱节点数组找到对应路径,通过距离数组得到最短值。

实现代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MaxSize 20
#define INFINITY 666

typedef char VertexType;

//定义图 的邻接矩阵表示法结构
typedef struct Graph {
    VertexType ver[MaxSize+1];// 顶点表,是一位数组
    int edg[MaxSize][MaxSize];// 邻接矩阵,可以看做是边表
}Graph;

//邻接矩阵法图的生成函数
void CreateGraph( Graph *g ){
    int i = 0;
    int j = 0;
    int VertexNum;
    VertexType Ver;
    while( '\n' != (Ver=getchar()) )
        g->ver[i++] = Ver;
    g->ver[i] = '\0';
    VertexNum = strlen(g->ver);
    for( i=0; i<VertexNum; i++ )
        for( j=0; j<VertexNum; j++ )
            scanf("%d", &g->edg[i][j]);

}

//求图的顶点数
int CalVerNum( Graph g ){
    return strlen(g.ver);
}

//将不邻接的顶点之间的权值设置为INFINITY
void SetWeight( Graph *g ){
    int i, j;
    for( i=0; i<CalVerNum(*g); i++ ){
        for( j=0; j<CalVerNum(*g); j++ ){
            if( -1 == g->edg[i][j] )
                g->edg[i][j] = INFINITY;
        }
    }
}


//Dijkstra求最短路径函数
void Dijkstra( Graph g ){
    int VertexNum = CalVerNum( g );
    int i, j, k, m, n;
    int mini;
    int *used = (int *)malloc(sizeof(int)*VertexNum);
    int *distance = (int *)malloc(sizeof(int)*VertexNum);
    int *parent = (int *)malloc(sizeof(int)*VertexNum);
    SetWeight( &g ); //设置权值
    printf("读取到的顶点依次为:");
    for( i=0; i<VertexNum; i++ ){
        printf(" %c  ",g.ver[i]);
    }
    printf("\n\n");
    
    printf("读取到的邻接矩阵为(666表示无穷大)\n");
    for( i=0; i<VertexNum; i++ ){
        for( j=0; j<VertexNum; j++ ){
            printf("%d  ",g.edg[i][j]);
        }
        printf("\n");
    }
    printf("\n");
    printf("请输入起点和终点(空格隔开):");
    char a,b;
    int st,en;
    fflush(stdin);
    scanf("%c %c",&a,&b);
    for( i=0; i<VertexNum; i++ ){
        if(g.ver[i] == a){
            st = i;
        }
        if(g.ver[i] == b){
            en = i;
        }
    }
    for( i=0; i<VertexNum; i++ ){
        used[i] = 0;
        parent[i] = 0;
        distance[i] = g.edg[st][i];//初始化为与编号为0的顶点的距离
    }
    used[st] = 1;
    for( i=0; i<VertexNum-1; i++ ){
        j = 0;
        mini = INFINITY;
        for( m=0; m<VertexNum; m++ ){
            if((0 == used[m]) && (distance[m] < mini) ) {
                mini = distance[m];
                j = m; //j为刚刚找到的V-U中到源点路径最短的顶点
            }
        }
        used[j] = 1;
        for( n=0; n<VertexNum; n++ ){
            if( (0 == used[n]) && (distance[n] > distance[j] + g.edg[j][n]) ){//由于有顶点新加入U集合,对距离数组distance进行更新,
                //比较原路径长度与以新加入的顶点为中间点的路径长度
                distance[n] = distance[j] + g.edg[j][n];
                parent[n] = j;
            }
        }
    }    
    char ans [20];
    int dq=st,dw=en;
    int pathnum=0;
    ans[pathnum++] = g.ver[dw];
    
    while(g.ver[dw]!=g.ver[dq]){
        dw=parent[dw];
        ans[pathnum++] = g.ver[dw] ;
    } 
    printf("\n路径是:");
    for( i=pathnum-1; i>=0; i-- ){
        if(i==0){
            printf("%c", ans[i]);
            break;
        } else{
            printf("%c ---> ", ans[i]);
        }
    } 
    printf("\n\n");
    if(distance[en]==666){
        printf("无路径到达\n");
    } else{
        printf("%c到%c最短路径长度为: %d\n", g.ver[st], g.ver[en], distance[en]);
    }
    
}

int main(){
    Graph g;
    freopen("in.txt","r",stdin);//切文件输入数据;
    CreateGraph( &g );
    freopen("CON","r",stdin); //切换到从控制台输入数据;
    Dijkstra( g );
    return 0;
}

 

 文件内容:

1234
0 1 -1 3
-1 0 2 4
-1 -1 0 5
-1 -1 -1 0

很经典,思想也很简单

 

posted @ 2021-01-08 00:05  西凉锦马超  阅读(225)  评论(0编辑  收藏  举报