dijkstra求最短路径长度

概述

dijkstra算法是单源最短路径算法的一种。

所谓单源,即在一个有向图中,从一个节点出发,算法可求该节点至所有可到达节点的最短路径长度。与之相对的称为非单源最短路,即算法运行一次可求出任意节点至任意可到达节点的最短路长度,其代表是floyd算法。
单源最短路有两种常见算法,dijkstra算法和bellman-ford算法。前者只能用于求路径权值全部为非负数的最短路,但后者是可以求边权中出现负数的算法。

既然非单源一次可以求出任意节点间的最短路,我们为什么还要使用单源算法呢?

因为floyd的时间复杂度为O(n^3),而dijkstra只有O(n^2)。如果要求出任意节点间的最短路,那么两个算法似乎都是O(n^3),然而floyd似乎更好理解。

dijkstra算法描述:

设G=(V,E)是一个有向图,V表示顶点,E表示边;它的每一条边(i,j)属于E,都有一个非负权W(i,j);在G中指定一个结点V0,要求求出从V0到G的每一个节点Vj(Vj属于V)的最短路径长度。

举例

根据下图,求从节点0开始到各节点的最短距离:

《dijkstra求最短路径长度》

配合图中右边的过程:

  1. 首先我们先将从节点0出发的所有路径长度标记为无穷大。
  2. 由于从节点0出发,故更新从节点0到其直接相邻节点的路径长度,0->1:10,0->3:30,0->4:100。
  3. 取出其中最小的0->1:10,并且我们已经清楚0->1的距离已经是最短的了。
    解释:从0到1无非两种走法,0直接到达1或0通过其他节点到达1。现在0到1的距离为10,其已小于0到其余节点的距离,即0->C->1=(30或100)+C->1,0->C->1已经不可能小于现在的0->1了,故现在的0->1已经是最短的,不可能有更短的0->1。
    接着我们看下从节点1到其余节点的路径,明显有一条1->2:50,通过1->2可以产生0->1->2:60这条路径,该路径长度远小于现在表里的0->2为无穷大,故更新0->2:60。
  4. 接下来取出表里最小且没有使用过的值,0->3:30。同理,从节点3到节点2、4有直接的路径,得出0->3->2:50,其小于表中的0->2:60,故更新;0->3->4:90,其小于表中的0->4:100,故更新。
  5. 到此时,0->1:10,0->2:50,0->3:30,0->4:90,其中节点0、1、3已经被使用过,所以下面我们取出表里的最小且没有使用过的值,0->2:50。同理,节点2到4有一条直接路径,0->2->4:100,但其大于表中的0->4:90,故不更新。

算法步骤

步骤一:初始路径长度均为无穷大,将出发节点作为节点B和A。
步骤二:找到A节点射出的路径,将A节点作为中途节点,C为任意节点。当B->A->C<B->C,更新表中B->C的长度。
步骤三:从表中选出最小且未作为过A节点的值,将其对应的节点作为A;重复步骤二,直至所有节点均被作为过A节点。

代码实现

根据上图的例子,给出对应的算法,求解从节点0到各节点的最短路长度:

#include<stdio.h>
const int N=100;
const int INF=100000;              //INF假定为无穷大
int p[N][N],d[N];                  //p表示各节点间的距离,不存在路径即为无穷大;d表示从出发节点到各节点的最短路径长度

void dijkstra(int sec,int n)       //sec为出发节点,n为图中的节点数
{
    int i,j,min,min_num;
    int vis[N]={0,};               //用于标记是否已作为过中途节点,0表示没有,1表示有
    for(i=0;i<n;i++)               //初始化
    {
        d[i]=p[sec][i];
    }
    vis[sec]=1;d[sec]=0;           //出发节点到自己的距离永远为0
    for(i=1;i<n;i++)
    {
        min=INF;
        for(j=0;j<n;j++)           //每次循环取出d数组中的未被作为过中途节点且数值最小的
        {
            if(!vis[j]&&d[j]<min)
            {
                min=d[j];          //更新最小值
                min_num=j;         //更新最小值所对应的节点,即记录下标
            }
        }
        vis[min_num]=1;            //标记该节点,表示其已被作为中途节点
        for(j=0;j<n;j++)           //循环,经过min_num节点到达是否有更小距离,如有更小距离则更新d数组
        {
            if(d[j]>min+p[min_num][j])
            {
                d[j]=min+p[min_num][j];
            }
        }
    }
}
int main()
{
    int i,j,n=5;                   //n表示图中的节点个数
    for(i=0;i<n;i++)               //程序用二维数组p存储各节点间的距离,这里则进行初始化
    {
        for(j=0;j<n;j++)
        {
            p[i][j]=(i==j?0:INF);  //初始化:i到j路径为无穷大或者i到i本身为0
        }
    }
    p[0][1]=10;p[0][3]=30;p[0][4]=100;p[1][2]=50;p[2][4]=50;p[3][2]=20;p[3][4]=60;  //p[i][j]表示节点i到节点j的距离
    dijkstra(0,n);                 //求从节点0出发到各节点的最短距离
    for(i=0;i<n;i++)               //打印从节点0出发到各节点的最短距离
    {
        printf(i==n-1?"%d\n":"%d ",d[i]);
    }
    return 0;
}

记录路径

如何在求最短路长度的同时记录路径呢?可以看这篇博客“dijkstra求最短路并记录路径

    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/jinixin/article/details/52233660
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞