Dijkstra算法 详细讲解

Dijkstra算法 详细解释html

Dijkstra算法适用于边权值为正的状况,若是边权值为负数就才用另外一种最短路算法Bellman-Ford算法。c++

该算法是指从单个源点到各个结点的最短路,该算法适用于有向图和无向图。算法

复杂度O(n^2)数组

伪代码:spa

////伪代码
清楚全部点的标号
所有d[i] = INF
而后将图信息权值复制到d中
循环n次{
    在全部为标号的结点中,选出d值最小的结点x
    给x标记
    对于从x出发的全部边(x,y),更新d[y] = min{d[y],d[y]+w(x,y)}
}

故而获得的代码模板:code

void dijkstra(int u)
{
    memset(v,0,sizeof v);
    memset(dis,INF,sizeof dis);
    v[u] = 1;
    for(int i = 1; i <= n; i++)
        dis[i] = min(dis[i],Map[u][i]);
    for(int i = 1; i <= n; i++)
    {
        int x,m = INF;
        for(int y = 1; y <= n; y++)
            if(!v[y] && dis[y] <= m)
                m = dis[x = y];
        v[x] = 1;
        for(int y = 1; y <= n; y++)
            dis[y] = min(dis[y],dis[x]+Map[x][y] );
    }
}

数据样例:htm

5 6 1
1 2 5
1 3 8
2 3 1
2 4 3
4 5 7
2 5 2

所描绘的图为:blog

下面模拟一下(来源 请进入):get

咱们以1为源点,来求全部点到一号点的最短路径。it

先创建一个dis数组,dis[i]表示第i号点到源点(1号点)的估计值,你可能会问为何是估计值,由于这个估计值会不断更新,更新到必定次数就变成答案了,这个咱们一会再说。

而后咱们在创建一个临界矩阵,叫作:map,map[i][j]=v表示从i到j这条边的权值是v。

dis初始值除了源点自己都是无穷大。源点自己都是0.

先从1号点开始。一号点,map[1][2]=5,一号点离2号点是5,比无穷大要小,因此dis[2]从无穷大变成了5。顺便,咱们用minn记录距离1号点最短的点,留着之后会用。

dis[0,5,∞,∞,∞]。minn=2。

而后搜到3号点,map[1][3]=8,距离是8,比原来的dis[3]的∞小,因而dis[3]=8。可是8比dis[2]的5要大,因此minn不更新。

dis[0,5,8,∞,∞]

接着分别搜索4,5号点,发现map[1][4],map[1][5]都是∞,因此就不更新。

如今,dis数组所呈现的明显不是最终答案,由于咱们才更新一遍,如今咱们开始第二次更新,第二次更新以什么为开始呢?就是以上一次咱们存下来的,minn,至关于把2当源点,求全部点到它的最短路,加上它到真正的源点(1号点)的距离,就是咱们要求的最短路。

从2号点开始,搜索3号点,map[2][3]=1,本来dis[3]=8,发现dis[2]+map[2][3]=5+1=6<dis[3](8)因此更新dis[3]为6,minn=3

dis[0,5,6,∞,∞] minn=3.

而后搜索4号点,map[2][4]=3,本来dis[4]=∞,因此,dis[2]+map[2][4]=5+3=8<dis[4](∞)因此更新dis[4]=8,由于map[2][4]=3,3>1,minn不更新。

dis[0,5,6,8,∞] minn=3.

接着搜索5号点,map[2][5]=2,5+2=7,7<∞,dis[5]=7minn不变。

dis[0,5,6,8,7]

二号点搜完,由于minn是3,继续搜索3号点。

三号点仍是按照二号点的方法搜索,发现没有能够更新的,而后搜索四号。

四号搜5号点,发现8+7>5+2,因此依然不更新,而后跳出循环。

 如今的估计值就所有为肯定值了:

dis[0,5,6,8,7]

这就是每一个点到源点一号点的距离,咱们来看一下代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 500;
const int INF = 0x3f3f3f3f;
int n,m,dis[maxn],Map[maxn][maxn],v[maxn];
void dijkstra(int u)
{
    memset(v,0,sizeof v);
    memset(dis,INF,sizeof dis);
    v[u] = 1;
    for(int i = 1; i <= n; i++)
        dis[i] = min(dis[i],Map[u][i]);
    for(int i = 1; i <= n; i++)
    {
        int x,m = INF;
        for(int y = 1; y <= n; y++)
            if(!v[y] && dis[y] <= m)
                m = dis[x = y];
        v[x] = 1;
        for(int y = 1; y <= n; y++)
            dis[y] = min(dis[y],dis[x]+Map[x][y] );
    }
}
int main()
{
    int a,u,v,w;
    scanf("%d%d%d",&n,&m,&a);
    memset(Map, INF, sizeof Map);
    for(int i = 0; i < m; i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        Map[u][v] = w;
        Map[v][u] = w; //该图为无向图,若为有向图则须要少创建一个边信息
    }
    for(int i = 1; i <= n; i++)
        Map[i][i] = 0;
    dijkstra(a);
    for(int i = 1; i <= n; i++)
        cout<<dis[i]<<" ";
    return 0;
}

上述算法采用邻接矩阵来存储边的信息,此种方法的时间复杂度为O(n^2)。

在不少状况中,图中的边并无那么多,mlog(n) 比 n^2 小的多。m 远小于 n^2的图称为稀疏图,而 m 相对较大的图为稠密图。

稀疏图适合使用vector数组来储存,还有一种表示方法即是——邻接表。