基础图论算法最短路径.docx

上传人:b****6 文档编号:8079381 上传时间:2023-01-28 格式:DOCX 页数:44 大小:174.15KB
下载 相关 举报
基础图论算法最短路径.docx_第1页
第1页 / 共44页
基础图论算法最短路径.docx_第2页
第2页 / 共44页
基础图论算法最短路径.docx_第3页
第3页 / 共44页
基础图论算法最短路径.docx_第4页
第4页 / 共44页
基础图论算法最短路径.docx_第5页
第5页 / 共44页
点击查看更多>>
下载资源
资源描述

基础图论算法最短路径.docx

《基础图论算法最短路径.docx》由会员分享,可在线阅读,更多相关《基础图论算法最短路径.docx(44页珍藏版)》请在冰豆网上搜索。

基础图论算法最短路径.docx

基础图论算法最短路径

基础图论算法

一、最短路算法

●Dijkstra:

单源最短路的算法,时间复杂度为O(V^2),稠密图效果好,稀疏图可以用二叉堆优化到O(VlogV)

●SPFA或Bellman-Ford(程序比较长,建议选学):

单源最短路的算法,时间复杂度为O(VE),但是往往远远达不到理论上界,稀疏图效果很好。

●Floyd:

每对顶点之间最短路的算法,时间复杂度为O(V^3),顶点数不能太多。

Dijkstra算法

简介:

Dijkstra算法是由荷兰计算机科学家艾兹格·迪科斯彻发现的。

算法解决的是有向图中最短路径问题。

算法介绍

Dijkstra算法是由荷兰计算机科学家艾兹格·迪科斯彻发现的。

算法解决的是有向图中最短路径问题。

举例来说,如果图中的顶点表示城市,而边上的权重表示两城市间开车行经的距离。

 Dijkstra算法可以用来找到两个城市之间的最短路径。

Dijkstra算法的输入包含了一个有权重的有向图G,以及G中的一个来源顶点S。

 我们以V表示G中所有顶点的集合。

 每一个图中的边,都是两个顶点所形成的有序元素对。

(u,v)表示从顶点u到v有路径相连。

 我们以E为所有边的集合,而边的权重则由权重函数w:

 E → [0, ∞]定义。

 因此,w(u,v)就是从顶点u到顶点v的非负花费值(cost)。

 边的花费可以想像成两个顶点之间的距离。

任两点间路径的花费值,就是该路径上所有边的花费值总和。

 已知V中有顶点s及t,Dijkstra算法可以找到s到t的最低花费路径(i.e. 最短路径)。

 这个算法也可以在一个图中,找到从一个顶点s到任何其他顶点的最短路径。

基本思想是:

设置一个顶点的集合s,并不断地扩充这个集合,一个顶点属于集合s当且仅当从源点到该点的路径已求出。

开始时s中仅有源点,并且调整非s中点的最短路径长度,找当前最短路径点,将其加入到集合s,直到终点在s中。

1.一般的dijkstra算法(邻接矩阵表示):

  programdijkstra;

  vara:

array[1..100,1..100]ofinteger;//用a[I,j]表示i与j之间的距离

  flag:

array[1..100]ofboolean;//用flag[i]表示i点是否被访问

  x,n,i,j,min,minn:

integer;//min临时存放当前最小的距离;minn临时存放最小距离点的序号

  begin

  readln(n);

  fori:

=1tondo

  begin

  forj:

=1tondoread(a[i,j]);

  readln;

  end;

  fillchar(flag,sizeof(flag),false);

  flag[1]:

=true;

  minn:

=1;

  forx:

=2tondo

  begin

  min:

=32767;

  fori:

=2tondo

  if(a[1,i]

  begin

  min:

=a[1,i];

  minn:

=i;

  end;

  flag[minn]:

=true;

  forj:

=1tondo

  if(j<>minn)and(a[1,minn]+a[minn,j]

=a[1,minn]+a[minn,j];

 end;

  fori:

=1tondo  write(a[1,i],'');

  end.

  4

  010030999

  10009910

  3099099

  99910990

  程序输出:

010030110

2.一般的dijkstra算法(邻接矩阵表示+最短的具体路径):

 

programzudlouj;

constn=6;max=10000;

cost:

array[1..6,1..6]ofreal=((0,50,10,max,45,max),(max,0,15,max,10,max),(20,max,0,15,max,max),(max,20,max,0,35,max),(max,max,max,30,0,max),(max,max,max,3,max,0));{连通关系,连通路径长度,不连通则赋值为max}

vardist:

array[1..n]ofreal; {起点到当前点的最短路径}

   path,p:

array[1..n]of1..n;{path[i]:

记录以i为终点的到i点能取得最短路径的起点}

   first,tail,u:

1..n;{起点与终点}

  s:

setof1..n;{存储已走过的点}

   i,j,y,m:

integer;

  min:

real;

begin

 read(first,tail);

 fori:

=1tondodist[i]:

=max;{一开始所有的点都赋值为∞表示没有走过}

 dist[first]:

=0;{起点赋值为0}

 s:

=[first];{起点已走过,存入s中}

u:

=first;{u表示当前子路径的起点位置}

 whileu<>taildo{当子路径的起点不是终点,即还没走到终点时}

  begin

  for j:

=1tondo

   ifnot(jins)and(dist[u]+cost[u,j]

=u),并且到j的最短路径dist[j]:

=dist[u]+cost[u,j]}

    begindist[j]:

=dist[u]+cost[u,j];path[j]:

=uend;

  min:

=max;

  forj:

=1tondo

   ifnot(jins)and(dist[j]

=j;min:

=dist[j];end;{找出当前子路径起点u所能到达的最短的j,并将j作为下一条子路径的起点u}

  ifmin=max{如果min=max即当前点到四周所有的点都没有通路(死路一条了)}thenbeginwriteln('Noanswer');haltend;

  s:

=s+[u];{将当前的子路径起点u放入s}

 end;

{输出}

writeln('mindist(',first,',',tail,')=',dist[tail]:

8:

2);

 y:

=tail;m:

=0;

while(y<>first)  do

  begininc(m);p[m]:

=y;y:

=path[y];end;{p[1]:

1为终点,p[m]为起点(从后到前寻找回路径)}

write('path:

',first);

forj:

=mdownto1do  write('->',p[j]);

writeln;

end.

3.堆优化的dijkstra算法(dijkstra+邻接表+heap) 

programSSSP;{singlesourceshortestpath}

{假设一个图的最大节点数为1000,所有运算在integer范围内}

{程序目标:

给定有向图的邻接表,求出节点1到节点n的最短路径长度}

constmaxn=1000;{最大节点数}

var

n:

integer;{节点个数}

deg:

array[1..maxn]ofinteger;{每个节点的度数}

list:

array[1..maxn,1..maxn,1..2]ofinteger;{邻接表,第一个元素表示边的终点,第二个元素表示边的长度}

count:

integer;{堆内元素个数计数器}

heap:

array[1..maxn]ofinteger;{heap[i]表示堆内的第i的元素的节点编号}

pos:

array[1..maxn]ofinteger;{表示编号为i的元素在堆内的位置}

key:

array[1..maxn]ofinteger;{表示节点1到节点i的最短距离}

exist:

array[1..maxn]ofboolean;{表示节点i是否存在于堆中}

i,j,now:

integer;

procedureswap(vari,j:

integer);{交换整数i和j}

vartemp:

integer;

begin

temp:

=i;

i:

=j;

j:

=temp;

end;

procedureheapify(p:

integer);{调整堆的过程,向下调整}

varbest:

integer;

begin

best:

=p;

if(p*2<=count)and(key[heap[p*2]]

=p*2;

if(p*2+1<=count)and(key[heap[p*2+1]]

=p*2+1;

ifbest<>pthen

begin

swap(pos[heap[p]],pos[heap[best]]);

swap(heap[p],heap[best]);

heapify(best);

end;

end;

proceduremodify(id,new_key:

integer);{判断new_key与key[id]大小,并修改key[id]大小,向上调整}

varp:

integer;

begin

if(new_key

begin

key[id]:

=new_key;

p:

=pos[id];

while(p>1)and(key[heap[p]]

begin

swap(pos[heap[p]],pos[heap[pdiv2]]);

swap(heap[p],heap[pdiv2]);

p:

=pdiv2;

end;

end;

end;

procedureextract(varid,dis:

integer);{读取堆中最小元素的节点编号和节点1到该节点的距离}

begin

id:

=heap[1];

dis:

=key[id];

dec(count);

if(count>0)then

begin

swap(pos[heap[1]],pos[heap[count+1]]);

swap(heap[1],heap[count+1]);

heapify

(1);

end;

end;

begin

{读取数据}

readln(n);

fori:

=1tondo

begin

read(deg[i]);

forj:

=1todeg[i]doread(list[i,j,1],list[i,j,2]);

end;

{初始化}

fori:

=1tondo

begin

exist[i]:

=true;

pos[i]:

=i;

heap[i]:

=i;

key[i]:

=maxint;

end;

count:

=n;

key[1]:

=0;

{dijkstra算法的主要操作}

while(count>0)do

begin

extract(i,now);

ifnow=maxintthenbreak;

forj:

=1todeg[i]do

ifexist[list[i,j,1]]thenmodify(list[i,j,1],now+list[i,j,2]);

end;

{输出}

ifkey[n]=maxintthenwriteln('NotConnected!

'){节点1和节点n不连通}

elsewriteln(key[n]);{连通}

end.

 

 

SampleInput

6

3250310545

2315510

2120415

2220535

1450

143

SPFA算法

求单源最短路的SPFA算法的全称是:

ShortestPathFasterAlgorithm。

最短路径快速算法-SPFA算法是西南交通大学段凡丁于1994年发表的。

适用范围:

给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。

我们约定有向加权图G不存在负权回路,即最短路径一定存在。

当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。

算法思想:

我们用数组d记录每个结点的最短路径估计值,用邻接表来存储图G。

我们采取的方法是动态逼近法:

设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。

这样不断从队列中取出结点来进行松弛操作,直至队列空为止。

实现方法:

建立一个队列,初始时队列里只有起始点,再建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。

然后执行松弛操作,用队列里有的点作为起始点去刷新到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。

重复执行直到队列为空。

判断有无负环:

如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图);我们还可以做一次拓扑排序,以判断是否存在负权回路(在没排完之前出现了没有入度为0的点,那么此图一定存在回路)。

单源最短路径中的松弛操作

对于图中的每个顶点v∈V,都设置一个属性d[v],描述从源点s到v的最短路径上权值的上界,称为最短路径估计。

pre[v]代表S到v的当前最短路径中v点之前的一个点的编号,我们用下面的过程来对最短路径估计和前趋进行初始化。

(O(V)时间)。

经过初始化以后,对所有v∈V,pre[v]=NULL,对v∈V-{s},有d[s]=0以及d[v]=∞。

实际松弛过程:

在松弛一条边(u,v)的过程中,要测试是否可以通过u节点,对迄今找到的v的最短路径进行改进;如果可以改进的话,则更新d[v]和pre[v]。

一次松弛操作可以减小最短路径估计的值d[v],并更新v的前趋域pre[v](S到v的当前最短路径中v点之前的一个点的编号)。

下面的伪代码对边(u,v)进行一步松弛操作。

融入实际的单源最短路径的算法中。

RELAX(u,v,w)

if(d[v]>d[u]+w(u,v))

{d[v]=d[u]+w(u,v);

pre[v]=u;}

松弛是改变最短路径和前趋的唯一方式。

 

求右图a到g的最短路径

首先建立起始点a到其余各点的最短路径表格

a

b

c

d

e

f

g

d[i]

0

首先源点a入队,当队列非空时:

1、队首元素(a)出队,对以a为起始点的所有边的终点依次进行松弛操作(此处有b,c,d三个点),此时路径表格状态为:

a

b

c

d

e

f

g

d[i]

0

24

8

15

在松弛时三个点的最短路径估值变小了,而这些点队列中都没有出现,这些点需要入队,此时,队列中新入队了三个结点b,c,d

队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e点),此时路径表格状态为:

a

b

c

d

e

f

g

d[i]

0

24

8

15

30

在最短路径表中,e的最短路径估值也变小了,e在队列中不存在,因此e也要入队,此时队列中的元素为c,d,e

队首元素c点出队,对以c为起始点的所有边的终点依次进行松弛操作(此处有e,f两个点),此时路径表格状态为:

a

b

c

d

e

f

g

d[i]

0

24

8

15

15

11

在最短路径表中,e,f的最短路径估值变小了,e在队列中存在,f不存在。

因此e不用入队了,f要入队,此时队列中的元素为d,e,f

队首元素d点出队,对以d为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:

a

b

c

d

e

f

g

d[i]

0

24

8

15

15

11

19

在最短路径表中,g的最短路径估值变小了,g在队列中不存在,因此g要入队,此时队列中的元素为e,f,g

队首元素e点出队,对以e为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:

 

a

b

c

d

e

f

g

d[i]

0

24

8

15

15

11

19

在最短路径表中,g的最短路径估值没有变小(松弛不成功),没有新结点入队,队列中元素为f,g

队首元素f点出队,对以f为起始点的所有边的终点依次进行松弛操作(此处有d,e,g三个点),此时路径表格状态为:

a

b

c

d

e

f

g

d[i]

0

24

8

15

13

11

14

在最短路径表中,e,g的最短路径估值又变小,队列中无e点,e入队,队列中存在g这个点,g不用入队,此时队列中元素为g,e

队首元素g点出队,对以g为起始点的所有边的终点依次进行松弛操作(此处只有b点),此时路径表格状态为:

 

a

b

c

d

e

f

g

d[i]

0

17

8

15

13

11

14

在最短路径表中,b的最短路径估值又变小,队列中无b点,b入队,此时队列中元素为e,b

队首元素e点出队,对以e为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:

 

a

b

c

d

e

f

g

d[i]

0

17

8

15

13

11

14

在最短路径表中,g的最短路径估值没变化(松弛不成功),此时队列中元素为b

队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e这个点),此时路径表格状态为:

a

b

c

d

e

f

g

d[i]

0

17

8

15

13

11

14

在最短路径表中,e的最短路径估值没变化(松弛不成功),此时队列为空了

最终a到g的最短路径为14

SPFA算法实现:

1.【邻接矩阵算法框架】

procedurespfa;

begin

fillchar(q,sizeof(q),0);h:

=0;t:

=0;//队列

fillchar(v,sizeof(v),false);//v[i]判断i是否在队列中

fori:

=1tondod[i]:

=maxint;//初始化最小值

inc(t);q[t]:

=1;v[1]:

=true;//点1入队并做标记

d[1]:

=0;//这里把1作为源点

whilenot(h=t)dobegin

  x:

=q[(h+1)modn];h:

=(h+1)modn;v[x]:

=false;//出队

   fori:

=1tondo//枚举x指向的点i

   if(a[x,i]>0)and(d[x]+a[x,i]

   begin

     d[i]:

=d[x]+a[x,i];//更新x指向点的最短路径长度

     ifnot(v[i])thenbegin//如果i不在队列中,则将其放入队列,改进与之相关的点

        t:

=(t+1)modn;q[t]:

=i;v[i]:

=true;

      end;

    end;

end;

end;

2.【用邻接表表示的SPFA的算法】标准SPFA过程

(以求某个结点s到某个结点t的最短路为例,稍加修改即为单源最短路)

Pascal语言代码

programspfaprg;

const

maxp=10000;{最大结点数}

var{变量定义}

p,c,s,t:

longint;{p,结点数;c,边数;s:

起点;t:

终点}

a,b:

array[1..maxp,0..maxp]oflongint;{a[x,y]存x,y之间边的权;b[x,c]存与x相连的第c个边的另一个结点y}

d:

array[1..maxp]ofinteger;{队列}

v:

array[1..maxp]ofboolean;{是否入队的标记}

dist:

array[1..maxp]oflongint;{到起点的最短路}

head,tail:

longint;{队首/队尾指针}

procedureinit;

vari,x,y,z:

longint;

begin

read(p,c);

fori:

=1tocdo

begin

readln(x,y,z);{x,y:

一条边的两个结点;z:

这条边的权值}

inc(b[x,0]);b[x,b[x,0]]:

=y;a[x,y]:

=z;{b[x,0]:

以x为一个结点的边的条数}

inc(b[y,0]);b[y,b[y,0]]:

=x;a[y,x]:

=z;

end;

readln(s,t);{读入起点与终点}

end;

procedurespfa(s:

longint);{SPFA}

vari,,j,now,sum:

longint;

begin

fillchar(d,sizeof(d),0);

fillchar(v,sizeof(v),false);

forj:

=1topdo

dist[j]:

=maxlongint;

dist[s]:

=0;v[s]:

=true;d[1]:

=s;{队列的初始状态,s为起点}

head:

=1;tail:

=1;

whilehead<=taildo{队列不空}

begin

now:

=d[head];{取队首元素}

fori:

=1tob[now,0]do

ifdist[b[now,i]]>dist[now]+a[now,b[now,i]]then

begin

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1