6.2 最短路径:Dijkstra算法与单源最短路径

Dijkstra算法——单源最短路径

Dijkstra算法是用来处理"指定一个点,计算该点到其余各个顶点的最短路径"这件事

简介

和上文有点像,因为当我们讨论一点到各个点的距离的时候,我们就不得不计算各种中转站
我们这里得到了一个新定义,松弛,我们认为它是:

如果两点距离通过中转点缩短了距离,我们就把这个过程叫松弛

Dijkstra算法实际就是 “选点,松弛,更新,选点” 的不断循环,直到得到结果

核心

我们这里使用两个数组
二维数组e来存储两点之间的路径关系
一维数组dis来存储一个点到其余各个点的初始路程(接下来会以1当例子)
我们暂且把dis中的值叫做“估计值”
我们先选择离1最近的点,得到其距离,和“估计值”比较,得到“确定值”
比如由1得到2,再由2得到其它边
然后更新、比较数据

步骤

1.将所有顶点分为两个部分。已知最短路径的集合P,未知的集合Q。
最开始P中只有源点一个顶点。我们可以用book数组来标记已知点

2.设置源点到自己的路径为0.
如果存在源点能直接到达的顶点i,则设置dis[i]为e[s][i]
把不能直接到达的顶点,设dis[i]为正无穷

3.在集合Q中选择一个离源点最近的顶点u(dis[u]最小)加入到P
检查所有以u为起点的边,每一条边都进行一次松弛的检查
边长为dis[u]+e[u][v],比较这个值和dis[u]的大小

4.重复第三步,直到Q为空,算法结束

代码示例

    #include <stdio.h>
    int main()
    {
        int e[10][10],dis[10],book[10],i,j,n,m,t1,t2,t3,u,v,min;
        int inf = 99999999 ; 
        
        scanf("%d %d",&n,&m);

        //信息初始化
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                if(i==j)    
                    e[i][j] = 0 ;
                else        
                    e[i][j] = inf ; 
        
        //读入边    
        for(i=1;i<=n;i++)
        {
            scanf("%d %d %d",&t1,&t2,&t3);
            e[t1][t2]=t3;
        }

        //初始化dis数组,这是1号顶点到达各个顶点的路程  
        for(i=1;i=n;i++)
            dis[i] = e[1][i];
        
        //book数组初始化
        for(i=1;i<n;i++)
            book[i] = 0 ;

        book[1]=1;

        //Dijkstra算法的核心语句  
        for(i=1;i<=n-1;i++)
        {
            //找到离1最近的顶点
            min = inf ; 
            for(j=1;j<-n;j++)
            {
                if(book[j]==0 && dis[j]<min)
                {
                    min = dis[j];
                    u = j ;
                }
            }

            book[u] = 1 ;//标记该点

            for(v=1;v<=n:v++)
            {
                if(e[u][v] < inf)
                {
                    //比较与数据更新
                    if(dis[v] > dis[u] + e[u][v])
                        dis[v] = dis[u] + e[u][v];
                }
            }
        }

        //输出最终结果
        for(i]1;i<=n;i++)
            printf("%d",dis[i]);

        getchar();gerchar();

        return 0 ;
    }

事实上,这是一种基于贪心策略的算法

邻接表

我们可以利用邻接表来存储一个图

    int n,m,i ; 
    //u,v和w的数组大小根据情况来设置,要比m的最大值大1;

    int u[6],v[6],w[6] ;
    //first和next的数组大小要根据实际情况来设置
    //first要比n的最大值大1
    //next要比m的最大值大1  

    int first[5],next[6];
    scanf("%d %d",&n,&m);

    //初始化first数组的下标1-n的数值为-1,表示1-n顶点暂时都没有边   

    for(i=1;i<=n;i++)
        first[i] = -1;
    
    for(i=1;i<=m;i++)
    {
        scanf("%d %d %d",&u[i],&v[i],&w[i]) ;  //读入每一条边   

        next[i] = first[u[i]];
        first[u[i]] = i ;
    }

用u,v,w三个数组来记录每条边的信息,即u[i],v[i],w[i]表示:
第i条边是从第u[i]号顶点到第v[i]顶点(u[i]->v[i]),且权值为w[i]
next[i]存储"编号为i的边"的"下一条边"的编号

posted @ 2021-11-19 11:01  RetenQ  阅读(47)  评论(0编辑  收藏  举报