Dijkstra算法(最短路算法)

迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉于19591959年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。

定义:

Dijkstra算法一般的表述通常有两种方式,一种用永久和临时标号方式,一种是用OPENOPEN,CLOSECLOSE表的方式,这里均采用永久和临时标号的方式。注意该算法要求图中不存在负权边。

原理:

1.首先,引入一个辅助向量DD,它的每个分量D_iDi​表示当前所找到的从起始点vv(即源点vv)到其它每个顶点v_ivi​的长度。

例如,D_3=2D3​=2表示从起始点到顶点3的路径相对最小长度为2。这里强调相对就是说在算法执行过程中D的值是在不断逼近最终结果但在过程中不一定就等于长度。

2.DD的初始状态为:若从vv到v_ivi​有弧(即从vv到v_ivi​存在连接边),则D_iDi​为弧上的权值(即为从vv到v_ivi​的边的权值);否则置D_iDi​为∞∞。

显然,长度为D_j=min\left \{ v_i \in V\right \}Dj​=min{vi​∈V}的路径就是从vv出发到顶点v_jvj​的长度最短的一条路径,此路径为(v,v_j)(v,vj​)。

3.那么,下一条长度次短的是哪一条呢?也就是找到从源点vv到下一个顶点的最短路径长度所对应的顶点,且这条最短路径长度仅次于从源点vv到顶点v_jvj​的最短路径长度。

假设该次短路径的终点是v_kvk​,则可想而知,这条路径要么是(v,v_kv,vk​),或者是(v,v_j,v_kv,vj​,vk​)。它的长度或者是从vv到v_kvk​的弧上的权值,或者是D_jDj​加上从v_jvj​到v_kvk​的弧上的权值。

4.一般情况下,假设SS为已求得的从源点vv出发的最短路径长度的顶点的集合,则可证明:下一条次最短路径(设其终点为xx)要么是弧(v,xv,x),或者是从源点vv出发的中间只经过SS中的顶点而最后到达顶点xx的路径。

因此,下一条长度次短的的最短路径长度必是D_j=min\left \{ D_i|v_i \in V-S \right \}Dj​=min{Di​∣vi​∈V−S},其中D_iDi​要么是弧(v,v_i)(v,vi​)上的权值,或者是D_k(v_k∈S)Dk​(vk​∈S)和弧(v_k,v_i)(vk​,vi​)上的权值之和。

算法描述如下:

1)令arcsarcs表示弧上的权值。若弧不存在,则置arcsarcs为∞∞(在本程序中为MAXCOSTMAXCOST)。SS为已找到的从vv出发的的终点的集合,初始状态为空集。那么,从vv出发到图上其余各顶点v_ivi​可能达到的长度的初值为D=arcs[Locate Vex(G,v_i)]D=arcs[LocateVex(G,vi​)],v_i∈Vvi​∈V;

2)选择v_jvj​,使得D_j=min(D|v_i∈V-S)Dj​=min(D∣vi​∈V−S);

3)修改从vv出发的到集合V-SV−S中任一顶点v_kvk​的最短路径长度。

问题描述:

在无向图G=(V,E)G=(V,E)中,假设每条边E_iEi​的长度为w_iwi​,找到由顶点V_0V0​到其余各点的最短值。

算法思想:

按路径长度递增次序产生算法:

把顶点集合VV分成两组:

(1)SS:已求出的顶点的集合(初始时只含有源点V_0V0​)

(2)V-S=TV−S=T:尚未确定的顶点集合

将T中顶点按递增的次序加入到SS中,保证:

(1)从源点V_0V0​到SS中其他各顶点的长度都不大于从V_0V0​到TT中任何顶点的最短路径长度

(2)每个顶点对应一个距离值

SS中顶点:从V_0V0​到此顶点的长度

TT中顶点:从V_0V0​到此顶点的只包括SS中顶点作中间顶点的最短路径长度

依据:可以证明V_0V0​到TT中顶点V_kVk​的,或是从V_0V0​到V_kVk​的直接路径的权值;或是从V_0V0​经SS中顶点到V_kVk​的路径权值之和

(反证法可证)

求最短路径步骤

算法步骤如下:

G=(V,E)G=(V,E)

1.初始时令S=(V_0)S=(V0​),T=V-S=T=V−S={其余顶点},TT中顶点对应的距离值

若存在<V_0V0​,V_iVi​>,d(V_0,V_i)d(V0​,Vi​)为<V_0V0​,V_iVi​>弧上的权值

若不存在<V_0V0​,V_iVi​>,d(V_0,V_i)d(V0​,Vi​)为∞∞

2.从TT中选取一个与SS中顶点有关联边且权值最小的顶点WW,加入到SS中

3.对其余TT中顶点的距离值进行修改:若加进WW作中间顶点,从V_0V0​到V_iVi​的距离值缩短,则修改此距离值

重复上述步骤2、3,直到SS中包含所有顶点,即W=V_iW=Vi​为止

1.0版标程:

#include<cstdio>
using namespace std;
int n,m,s,tot,dis[10003],head[500003],mi,t;
bool vis[10003];
struct edge {
    int next,to,w;
    edge() {
        this->next=this->to=this->w=0;
    }
} h[500003];
void add(int u,int v,int w) {
    h[++tot].next=head[u];
    h[tot].to=v;
    h[tot].w=w;
    head[u]=tot;
}
void dijkstra() {
    for(int i=1; i<=n; i++) {
        dis[i]=2147483647;
    }
    dis[s]=0;
    for(int i=1; i<=n; i++) {
        mi=2147483647;
        t=-1;
        for(int j=1; j<=n; j++) {
            if(!vis[j]&&dis[j]<mi) {
                mi=dis[j];
                t=j;
            }
        }
        if(t==-1) {
            break;
        }
        vis[t]=1;
        for(int j=head[t]; j; j=h[j].next) {
            if(!vis[h[j].to]&&dis[h[j].to]>(h[j].w+dis[t])) {
                dis[h[j].to]=h[j].w+dis[t];
            }
        }
    }
}
int main() {
    scanf("%d%d%d",&n,&m,&s);
    for(int i=0; i<m; i++) {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    dijkstra();
    for(int i=1; i<=n; i++) {
        printf("%d ",dis[i]);
    }
    return 0;
}

堆优化:

思考:

该算法复杂度为n^2n2,我们可以发现,如果边数远小于n^2n2,对此可以考虑用堆这种数据结构进行优化,取出最短路径的复杂度降为O(1)O(1);每次调整的复杂度降为O(elogn)O(elogn);ee为该点的边数,所以复杂度降为O((m+n)logn)O((m+n)logn)。

实现:

1.将与源点相连的点加入堆,并调整堆。

2.选出堆顶元素uu(即代价最小的元素),从堆中删除,并对堆进行调整。

3.处理与uu相邻的,未被访问过的,满足三角不等式的顶点

1):若该点在堆里,更新距离,并调整该元素在堆中的位置。

2):若该点不在堆里,加入堆,更新堆。

4.若取到的uu为终点,结束算法;否则重复步骤2、3。

2.0版标程:

#include<cstdio>
#include<queue>
using namespace std;
int head[100003],d[100003],ver[200003],edge[200003],Next[200003],n,m,tot,x,i,y,z,s,w,u,k;
bool v[100003];
priority_queue<pair<int,int> >q;
inline void add(int x,int y,int z) {
    ver[++tot]=y;
    edge[tot]=z;
    Next[tot]=head[x];
    head[x]=tot;
}
int main() {
    scanf("%d%d%d",&n,&m,&s);
    while(m--) {
        scanf("%d%d%d",&u,&k,&w);
        add(u,k,w);
    }
    for(i=1; i<=n; ++i) {
        d[i]=2147483647;
    }
    d[s]=0;
    q.push(make_pair(0,s));
    while(q.size()) {
        x=q.top().second;
        q.pop();
        if(v[x]) {
            continue;
        }
        v[x]=1;
        for(i=head[x]; i; i=Next[i]) {
            y=ver[i];
            z=edge[i];
            if(d[y]>d[x]+z) {
                d[y]=d[x]+z;
                q.push(make_pair(-d[y],y));
            }
        }
    }
    for(i=1; i<=n; ++i) {
        printf("%d ",d[i]);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_42321579/article/details/82932134
今日推荐