最短路:dijkstra算法+路径输出

Dijkstra(迪杰斯特拉)算法: 即给定图和起点,通过算法得到起点到其余点的最短路径。主要步骤就是:每次从剩余顶点中选一个离起点最近的点,然后更新这个点周围的点离起点的距离,同时标记这个点。直到所有的点都被标记。

  • 原理———之前看的一篇博客中有句解释这个过程的话感觉很棒:因为目前离 v1顶点最近的是 v3顶点,并且这个图所有的边都是正数,那么肯定不可能通过第三个顶点中转,使得 v1顶点到 v3顶点的路程进一步缩短了。因为 v1顶点到其它顶点的路程肯定没有 v1到 v3顶点短
    如图:
    最短路:dijkstra算法+路径输出_第1张图片
    按照算法的思路途中4个结点的遍历顺序是:v0、v1、v3、v2。第一次遍历过后我们选择了距离起点12的点v1,容易想到,我们不可能通过其他中转点,使得v0到v1的距离更短,但是我们可能通过v1这个中转点使得v0到v2或v3的距离更短。

  • 如何记录路径——在算法的实现过程中,可以想到,我们得到的是一棵最短路径树,根据树的特点,每个结点都只有一个父亲结点,所以我们就可以用一个一维数组存储路径,最后从终点开始逐步向上层遍历到根节点(即起点),从而得到路径。如上图,第一次遍历过后v1、v2、v3的父亲结点都是v0;第二次遍历v1点时,发现v0通过v1到v2(v0–>v1–>v2)比v0直接到v2(v0–v2)更近,便更新v2的父亲结点为v1。
    第一次遍历结束path数组:

0 1 2 3
-1 0 0 0

第二次遍历结束path数组:

0 1 2 3
-1 0 1 0
  • 代码如下:
#include 
#include 
#include 
#include
using namespace std;
#define MAX 100
#define INF 0x3f3f3f3f
#define me(a,b) memset(a,b,sizeof(b))
int dist[MAX],path[MAX];//储存最短距离和路径
struct MGraph
{
    int edges[MAX][MAX];//邻接矩阵,记录的是两点之间的距离,也就是权值
    int e,n;//边数和顶点数
} G;
void init()
{
    memset(G.edges,INF,sizeof(G.edges));
    //me(G.edges,(INF));
    for(int i=0; i<G.n; i++)
        G.edges[i][i]=0;
}
void printf_MG()
{
    for(int i=0; i<G.n; i++)
    {
        for(int j=0; j<G.n; j++)
        {
            if(G.edges[i][j]==INF)
                printf("∞ ");
            else
                printf("%2d ",G.edges[i][j]);
        }
        printf("\n");
    }
}
void Dijkstra(MGraph g,int u)
{
    int U[MAX],mmin;//分别表示已经遍历过的点、距当前起始点最近的点的距离
    //对各数组进行初始化
    memset(U,0,sizeof(U));
    memset(path,-1,sizeof(path));
    //me(dist,INF);
    for(int i=0; i<g.n; i++)
    {
        dist[i]=g.edges[u][i];
        if(g.edges[u][i]<INF)
            path[i] =u;
    }
    dist[u]=0;//到本身的距离
    for(int i=0; i<g.n; i++) //求出源点到n个点的最短距离
    {
        mmin=INF;
        U[u]=1;//将选出的新的起始点放入U数组中
        for(int j=0; j<g.n; j++)
            //这个if判断顶点u的加入是否会出现通往顶点j的更短的路径,如果出现,则改变原来路径及其长度,否则什么都不做
        {
            if(!U[j]&&dist[u]+g.edges[u][j]<dist[j])
            {
                dist[j]=dist[u]+g.edges[u][j];//更新路径长度
                path[j]=u;//更新到顶点j的路径
            }
        }
        for(int j = 0; j < g.n; j++)
            //这个循环每次从剩余顶点中选出一个顶点,通往这个顶点的路径在通往所有剩余顶点的路径中是长度最短的
        {
            if(U[j] == 0 && dist[j] < mmin)
            {
                u = j;
                mmin = dist[j];
            }
        }
    }
}
void printf_path(int u,int x)
{
    int a[MAX],cou=0,ex=x;
    if(u==x)
        printf("%d-->%d=0",u,x);
    else if(path[x]==-1)
        printf("%d-->%d=∞",u,x);//没有路径
    else
    {
        while(x!=u)
        {
            a[cou++]=x;
            x=path[x];
        }
        a[cou]=x;
        for(int i=cou; i>0; i--)
            printf("%d-->",a[i]);
        printf("%d=%d",a[0],dist[ex]);
    }
    printf("\n");
}
int main()
{
    int v1,v2,w;
    scanf("%d%d",&G.e,&G.n);//输入边数和顶点数
    init();
    for(int i=0; i<G.e; i++)
    {
        scanf("%d%d%d",&v1,&v2,&w);
        G.edges[v1][v2]=w;
        G.edges[v2][v1]=w;
    }
    printf_MG();//输出邻接矩阵
    int u;
    scanf("%d",&u);//输入源点
    Dijkstra(G,u);
    for(int i=0; i<G.n; i++)//输出源点到每个点的最短路径以及路径长路
        printf_path(u,i);
    return 0;
}
/*
13 10
0 1 10
0 2 15
1 3 20
1 4 5
2 5 8
2 6 6
3 7 7
4 5 10
4 7 3
4 8 4
5 6 9
5 8 3
6 8 12
*/

样例对应的无向图(因为两个结点之间可以互通,所以存储的时候需要双向存储)
最短路:dijkstra算法+路径输出_第2张图片
样例输出:
最短路:dijkstra算法+路径输出_第3张图片

  • 如果只需要得到起点到一个顶点的最短路径,把子函数void Dijkstra(MGraph g,int u)中最外层循环for(int i=0; iwhile(!U[i]),即当终点是剩余点中距离起点最近的点时,就可结束循环。

你可能感兴趣的:(最短路)