zoukankan      html  css  js  c++  java
  • 最短路径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( '
    ' != (Ver=getchar()) )
            g->ver[i++] = Ver;
        g->ver[i] = '';
        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("
    
    ");
        
        printf("读取到的邻接矩阵为(666表示无穷大)
    ");
        for( i=0; i<VertexNum; i++ ){
            for( j=0; j<VertexNum; j++ ){
                printf("%d  ",g.edg[i][j]);
            }
            printf("
    ");
        }
        printf("
    ");
        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("
    路径是:");
        for( i=pathnum-1; i>=0; i-- ){
            if(i==0){
                printf("%c", ans[i]);
                break;
            } else{
                printf("%c ---> ", ans[i]);
            }
        } 
        printf("
    
    ");
        if(distance[en]==666){
            printf("无路径到达
    ");
        } else{
            printf("%c到%c最短路径长度为: %d
    ", 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

    很经典,思想也很简单

  • 相关阅读:
    横向技术分析C#、C++和Java优劣
    XML数据流主动服务系统的设计与实现
    ASP.NET 状态管理
    Oracle中如何从BasicFile迁移到SecureFile
    让UpdatePanel支持上传文件
    C#和Visual Basic渐行渐远之匿名类型
    wpf 移动变换
    WPF 样式学习总结
    临江仙
    wpf 动画效果
  • 原文地址:https://www.cnblogs.com/mvpmvp/p/14249564.html
Copyright © 2011-2022 走看看