图论之 最短路

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

图论之最短路

一、求最短路方法(对于一个包含环的图)

1、Dijkstra

2、Bellman-ford

3、SPFA

4、Floyd

二、Dijkstra思想(求单源点最短路,不含负边权)

1、设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径, 就将其加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v 到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。

2、Dijkstra步骤

(1)初始时,S只包含源点,即S=v,距离为0。U包含除v外的其他顶点,U 中顶点u距离为边上的权;

(2)从U中选取一个距离v最小的顶点k,把k加入S中(该选定的距离就是v到k的最短路径长度);

(3)以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值为经过顶点k的值(松弛操作);

(4)重复步骤(2)和(3)直到所有顶点都包含在S中。

3、Dijkstra两种实现方法

(1)邻接矩阵+找最小边

(2)邻接表+优先队列

关键:松弛操作

if(d[v]>d[u]+e[u][v].w)

d[v]=d[u]+e[u][v].w

4、Dijkstra 稠密图的邻接矩阵

for(int i=0;i

{

int x,m=inf;

for(int y=0;y

m=dis[y];x=y;

vis[x]=1;

for(int y=0;ydis[x]+w[x][y])

dis[y]=dis[x]+w[x][y];

}

5、邻接链表+优先队列

memset(dis,127,sizeof(dis));

dis[1]=0;

q.push(make_pair(dis[1],1));

while(!q.empty())

{

int u=q.top().second;q.pop();

if(vis[u]) continue;

vis[u]=1;

for(int k=head[u];k!=-1;k=e[k].next)

{ if(dis[e[k].v]>dis[u]+e[k].w)

{ dis[e[k].v]=dis[u]+e[k].w;

q.push(make_pair(dis[e[k].v],e[k].v));

}

}

}

6、路径输出

方法一:从终点出发,不断顺着dis[y]==dis[x]+w[x][y]的边从y回到x,直到回到起点,但更好的方法是

方法二:在更新时维护father指针

for(int y=0;y

if(dis[y]>dis[x]+w[x][y])

father[y]=x;

7、邻接表+优先队列的dijkstra

(1)根据算法,我们需要在找到最短dis的节点序号v,所以入队的一个元素包含dis和v两部分,那么我们使用pair将捆绑两个整形:typedef pair pii (2)然后定义一个pii型的从小到大排列的优先队列(priority_queue ,greater > q; )

(3)注意:此时在元素入队时要加make_pair。如将(d[1],0)入队:q.push(make_pair(d[1],0));

(4)邻接矩阵的算法显然是O(N^2)的

稀疏图的邻接表

如果一个图的顶点很多,那它往往是稀疏图,那么使用邻接表和优先队列可以优化到O(MlogN)。

三、Bellman-ford(思想求单源点最短路,可含负边权)

1、算法步骤:

(1)初始化:将除源点外的所有顶点最短距离估计值d[v]=inf,d[s]=0;

(2)迭代求解:反复对边集E中每条边进行松弛操作,使得顶点集V中每个顶点v的最短距离估计值逐步逼近其最短距离;

(3)检验负权回路:如果有存在点v,使得d[v]>d[u]+w[u][v],则有负权回路,返回false;

(4)返回true,源点到v的最短距离保存在d[v]中。

2、伪代码

bool bellman-ford(G,w,s)

{

for each vertex in V(G)d[v]=inf;d[s]=0;

for(i=1;i

for if(d[v]>d[u]+w[u][v])d[v]=d[u]+w[u][v];

for each edge(u,v) in E(G)//v-1次松弛结束若还可以松弛,则有负环

if(d[v]>d[u]+w[u][v])return false;

return true;

}

四、SPFA思想(使用队列优化后的Bellman-ford)

1、用一个队列来进行维护。

流程:初始时,将源点入队,每次从队列中取出一个元素,并对其所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队,直到队列为空时算法结束。

可以证明用队列维护的Bellman-Ford最坏情况下时间为O(nm),通常时间为O(km),其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。2、伪代码

q.push(s);vis[s]=1;

void spfa()

{

while(!q.empty())

u=q.front();q.pop();vis[u]=0;//出队标记

for each v in adj(u)

{if(dis[v]>d[u]+w[u][v])

{

dis[v]=d[u]+w[u][v];

if(!vis[v]){q.push(v);vis[v]=1;}

}

}

五、floyd思想(求各点间的最短路,可含负边权)

1、动态规划原理

设d(i,j)为从i到j的只以(1…k)集合中的节点为中间点的最短路的长度;

(1)若经过k,d(i,j)=d(i,k)+d(k,j)

(2)若不经过k(可能经过1~k-1中的点),d(i,j)=d(i,j)

则d(i,j)=min(d(i,k)+d(k,j), d(i,j))

2、伪代码

for(k=0;k

for(i=0;i

for(j=0;j

if(d[i][j]>d[i][k]+d[k][j])

d[i][j]= d[i][k]+d[k][j];

注:初始时,d[i][i]=0,其他d值为inf

六、三种最短路算法

1、dijkstra(单源点最短路):贪心的思想,每次从集合U中找一个离源点最近的点加入到集合S中,并以新加入的点作为中间点松弛U中的点到源点的距离。直到U为空算法结束。

使用优先队列优化。队列中存储的是U中点的子集。

不能处理负权存在的情况。

复杂度远小于O(M*N),因为贪心所以速度三者中最快,用二项堆优化到O(ElogV)。

相关文档
最新文档