dijkstra求最短路并记录路径

dijkstra算法是单源最短路算法的一种,可用于求从出发节点到所有可到达节点的最短路长度。

下面我们来看下如何记录最短路的路径,有关dijkstra求最短路长度的过程,可以看这篇博客“dijkstra求最短路径长度”。


举例


下图中直接给出记录路径的过程,相比于求解最短路长度,只多了一个path数组(初始化为-1)用于记录路径。


分析


从上图可以看到,从出发节点0到节点5取得最小长度65,所经过的路径是0,3,2,4,5。

图中通过path数组来记录路径,path[i]=j表明节点i取得最小路径时,其最后一段走的是节点j到节点i。

你也许会疑惑,我想知道的是整个路径呀,记录其中的最后一段有什么用呢?

  • 我们这样来看,path[5]=4表明0->5的最短路径最后走的一段是4->5;同理path[4]=2确定0->4的最短路径的最后一段是由节点2到达节点4;那么通过path[2]=3可以得到0->2最短路径最后一段是由节点3到达节点2;而path[3]=-1表示从出发节点0有一条直接路径连接到节点3。在这个过程中,我们获得经过的路径是倒序的,所以给出答案时需要反转,故从出发节点0到节点5所经过的最短路径是0,3,2,4,5。

但你也许又有一个疑问,path[5]=4表明0->5的最短路径最后一段是4->5,可是你怎么知道0->4的最短路径与0->5的最短路径在0->4段走的是相同的路径呢?

  • 首先0->5的最短路径是0,3,2,4,5,path[5]=4表明0->5最短路径的最后一段是4->5,其中4->5必定只有一条直接路径,所以可以推得0->4的最短路径为0,3,2,4;因为如果0->4有更短的路径0->X->4,那么0->5的最短路径必定也会变成0->X->4->5。
  • 从图中也可以看到,path[i]并不是一旦赋值就不会改变的;只有i被作为了中途节点,那么path[i]才不会再改变,即出发节点到节点i的最小长度被确定后。

注意:图中初始化时path全为-1,并不是因为其出发节点为-1。比如path[i]=-1表明的是出发节点到节点i是直接路径,没有中途节点;并不代表从节点-1可以到节点i,也不代表-1是出发节点。当然初始值可以任意替换,只要注意更改“while(path[j]!=-1)”即可。


代码实现


#include<stdio.h>
#include<string.h>
#include<stack>
using namespace std;
const int N=100;
const int INF=100000;
int p[N][N],d[N],path[N];       //path数组用于记录路径

void dijkstra(int sec,int n)    //sec为出发节点,n表示图中节点总数
{
    int i,j,min,min_num;
    int vis[N]={0,};
    for(i=0;i<n;i++)
    {
        d[i]=p[sec][i];
    }
    vis[sec]=1;d[sec]=0;
    for(i=1;i<n;i++)
    {
        min=INF;
        for(j=0;j<n;j++)
        {
            if(!vis[j]&&d[j]<min)
            {
                min=d[j];
                min_num=j;
            }
        }
        vis[min_num]=1;
        for(j=0;j<n;j++)
        {
            if(d[j]>min+p[min_num][j])
            {
                path[j]=min_num;//path[j]记录d[j]暂时最短路径的最后一个中途节点min_num,表明d[j]最后一段从节点min_num到节点j
                d[j]=min+p[min_num][j];
            }
        }
    }
}
void print(int sec,int n)       //sec为出发节点,n表示图中节点总数
{
    int i,j;
    stack<int> q;               //由于记录的中途节点是倒序的,所以使用栈(先进后出),获得正序
    for(i=1;i<n;i++)            //打印从出发节点到各节点的最短距离和经过的路径
    {
        j=i;
        while(path[j]!=-1)      //如果j有中途节点
        {
            q.push(j);          //将j压入堆
            j=path[j];          //将j的前个中途节点赋给j
        }
        q.push(j);
        printf("%d=>%d, length:%d, path: %d ",sec,i,d[i],sec);
        while(!q.empty())       //先进后出,获得正序
        {
            printf("%d ",q.top());//打印堆的头节点
            q.pop();            //将堆的头节点弹出
        }
        printf("\n");
    }
}
int main()
{
    memset(path,-1,sizeof(path));//将path数组初始化为-1
    int i,j,n=6;
    for(i=0;i<n;i++)
    {
        for(j=0;j<n;j++)
        {
            p[i][j]=(i==j?0:INF);
        }
    }
    p[0][1]=10;p[0][3]=30;p[1][2]=50;p[1][4]=100;p[2][4]=5;p[3][2]=20;p[3][4]=60;p[4][5]=10;//p[i][j]表示节点i到节点j的距离
    dijkstra(0,n);               //求从节点0出发到各节点的最短距离
    print(0,n);                  //打印从节点0出发到各节点的最短距离和路径
    return 0;
}

代码运行结果:



Floyd算法和Dijkstra算法都是用来解图中最短路径问题的算法,但它们在适用情况、时间复杂度以及工作原理上有明显的区别。 1. **适用情况**: - **Floyd算法**是一个动态规划算法,可以解决包含负权边的图的单源最短路径问题,也能够处理所有顶点对之间的最短路径问题,即计算图中任意两点间的最短路径。 - **Dijkstra算法**通常用于带权有向图或无向图的单源最短路径问题,它不能处理包含负权边的图。该算法从一个源点开始,计算到其他所有顶点的最短路径。 2. **时间复杂度**: - **Floyd算法**的时间复杂度为O(n^3),其中n是顶点的数量。由于Floyd算法需要遍历所有顶点对,因此在处理大型图时可能会非常慢。 - **Dijkstra算法**的时间复杂度取决于所用的数据结构,通常使用优先队列(如最小堆)实现,可以达到O((V+E)logV)的时间复杂度,其中V是顶点数量,E是边的数量。因此,Dijkstra算法在单源最短路径问题中通常比Floyd算法更高效。 3. **工作原理**: - **Floyd算法**通过逐步更新一个“距离矩阵”来计算从一个顶点到另一个顶点的最短路径。该算法逐个引入每个顶点作为中间顶点,并更新任意两点间的最短路径长度,最终得到所有顶点对之间的最短路径。 - **Dijkstra算法**从源点开始,维护一个距离表,记录从源点到各个顶点的最短距离,并使用优先队列来选择当前已知的最短路径。算法不断更新这个距离表,直到所有顶点的最短路径都被找到。 总结来说,Floyd算法适用于计算图中任意两点间的最短路径,而Dijkstra算法适用于单源最短路径问题。在实际应用中,根据问题的不同,选择合适的算法以获得最优解。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值