单源最短路径两种方法教学.docx

上传人:b****6 文档编号:5670597 上传时间:2022-12-31 格式:DOCX 页数:11 大小:94.86KB
下载 相关 举报
单源最短路径两种方法教学.docx_第1页
第1页 / 共11页
单源最短路径两种方法教学.docx_第2页
第2页 / 共11页
单源最短路径两种方法教学.docx_第3页
第3页 / 共11页
单源最短路径两种方法教学.docx_第4页
第4页 / 共11页
单源最短路径两种方法教学.docx_第5页
第5页 / 共11页
点击查看更多>>
下载资源
资源描述

单源最短路径两种方法教学.docx

《单源最短路径两种方法教学.docx》由会员分享,可在线阅读,更多相关《单源最短路径两种方法教学.docx(11页珍藏版)》请在冰豆网上搜索。

单源最短路径两种方法教学.docx

单源最短路径两种方法教学

单源最短路径

问题描述

给定带权有向图G=(V,E),其中每条边的权是非负实数。

另外,还给定V中的一个顶点,称为源。

现在要计算从源到其他所有顶点的最短路长度。

这里路的长度是指路上各边权之和。

这个问题通常称为单源最短路径问题。

1、问题分析

推导过程(最优子结构证明,最优值递归定义)

1、贪心算法

对于图G,如果所有Wij≥0的情形下,目前公认的最好的方法是由Dijkstra于1959年提出来的。

已知如下图所示的单行线交通网,每弧旁的数字表示通过这条单行线所需要的费用,现在某人要从v1出发,通过这个交通网到v8去,求使总费用最小的旅行路线。

Dijkstra方法的基本思想是从vs出发,逐步地向外探寻最短路。

执行过程中,与每个点对应,记录下一个数(称为这个点的标号),它或者表示从vs到该点的最短路的权(称为P标号)、或者是从vs到该点的最短路的权的上界(称为T标号),方法的每一步是去修改T标号,并且把某一个具T标号的改变为具P标号的点,从而使G中具P标号的顶点数多一个,这样至多经过n-1(n为图G的顶点数)步,就可以求出从vs到各点的最短路。

在叙述Dijkstra方法的具体步骤之前,说明一下这个方法的基本思想。

s=1。

因为所有Wij≥0,故有d(v1,v1)=0。

这时,v1是具P标号的点。

现在考察从v1发出的三条弧,(v1,v2),(v1,v3)和(v1,v4)。

(1)如果某人从v1出发沿(v1,v2)到达v2,这时需要d(v1,v1)+w12=6单位的费用;

(2)如果他从v1出发沿(v1,v3)到达v3,这时需要d(v1,v1)+w13=3单位的费用;

(3)若沿(v1,v4)到达v4,这时需要d(v1,v1)+w14=1单位的费用。

因为min{d(v1,v1)+w12,d(v1,v1)+w13,d(v1,v1)+w14}=d(v1,v1)+w14=1,可以断言,他从v1到v4所需要的最小费用必定是1单位,即从v1到v4的最短路是(v1,v4),d(v1,v4)=1。

这是因为从v1到v4的任一条路P,如果不是(v1,v4),则必是先从v1沿(v1,v2)到达v2,或者沿(v1,v3)到达v3。

但如上所说,这时他已需要6单位或3单位的费用,不管他如何再从v2或从v3到达v4,所需要的总费用都不会比1小(因为所有wij≥0)。

因而推知d(v1,v4)=1,这样就可以使v4变成具P标号的点。

(4)现在考察从v1及v4指向其余点的弧,由上已知,从v1出发,分别沿(v1,v2)、(v1,v3)到达v2,v3,需要的费用分别为6与3,而从v4出发沿(v4,v6)到达v6所需的费用是d(v1,v4)+w46=1+10=11单位。

因min{d(v1,v1)+w12,d(v1,v1)+w13,d(v1,v4)+w46}=d(v1,v1)+w13=3。

基于同样的理由可以断言,从v1到v3的最短路是(v1,v3),d(v1,v3)=3。

这样又可以使点v3变成具P标号的点,如此重复这个过程,可以求出从v1到任一点的最短路。

在下述的Dijstra方法具体步骤中,用P,T分别表示某个点的P标号、T标号,si表示第i步时,具P标号点的集合。

为了在求出从vs到各点的距离的同时,也求出从Vs到各点的最短路,给每个点v以一个λ值,算法终止时λ(v)=m,表示在Vs到v的最短路上,v的前一个点是Vm;如果λ(v)=∞,表示图G中不含从Vs到v的路;λ(Vs)=0。

Dijstra方法的具体步骤:

{初始化}

i=0

S0={Vs},P(Vs)=0λ(Vs)=0

对每一个v<>Vs,令T(v)=+∞,λ(v)=+∞,

k=s

{开始}

①如果Si=V,算法终止,这时,每个v∈Si,d(Vs,v)=P(v);否则转入②

②考察每个使(Vk,vj)∈E且vjSi的点vj。

如果T(vj)>P(vk)+wkj,则把T(vj)修改为P(vk)+wkj,把λ(vj)修改为k。

③令

如果,则把的标号变为P标号,令,k=ji,i=i+1,转①,否则终止,这时对每一个v∈Si,d(vs,v)=P(v),而对每一个。

在下图所给的有向图G中,每一边都有一个非负边权。

要求图G的从源顶点s到目标顶点t之间的最短路径。

2、分支限界法

算法从图G的源顶点s和空优先队列开始。

结点s被扩展后,它的儿子结点被依次插入堆中。

此后,算法从堆中取出具有最小当前路长的结点作为当前扩展结点,并依次检查与当前扩展结点相邻的所有顶点。

如果从当前扩展结点i到顶点j有边可达,且从源出发,途经顶点i再到顶点j的所相应的路径的长度小于当前最优路径长度,则将该顶点作为活结点插入到活结点优先队列中。

这个结点的扩展过程一直继续到活结点优先队列为空时为止。

在算法扩展结点的过程中,一旦发现一个结点的下界不小于当前找到的最短路长,则算法剪去以该结点为根的子树。

在算法中,利用结点间的控制关系进行剪枝。

从源顶点s出发,2条不同路径到达图G的同一顶点。

由于两条路径的路长不同,因此可以将路长长的路径所对应的树中的结点为根的子树剪去。

2、计算求解过程、算法实现(源代码实现相关功能)

1、贪心算法

#include

#include

usingnamespacestd;

#defineMAX1000000//充当"无穷大"

#defineLENsizeof(structV_sub_S)

#defineN5

#defineNULL0

ints;//输入的源点

intD[N];//记录最短路径

intS[N];//最短距离已确定的顶点集

constintG[N][N]={{0,10,MAX,30,100},

{MAX,0,50,MAX,MAX},

{MAX,MAX,0,MAX,10},

{MAX,MAX,20,0,60},

{MAX,MAX,MAX,MAX,0}};

typedefstructV_sub_S//V-S链表

{

intnum;

structV_sub_S*next;

};

structV_sub_S*create()

{

structV_sub_S*head,*p1,*p2;

intn=0;

head=NULL;

p1=(V_sub_S*)malloc(LEN);

p1->num=s;

head=p1;

for(inti=0;i

{

if(i!

=s)

{

++n;

if(n==1)

head=p1;

else

p2->next=p1;

p2=p1;

p1=(V_sub_S*)malloc(LEN);

p1->num=i;

p1->next=NULL;

}

}

free(p1);

returnhead;

}

structV_sub_S*DelMin(V_sub_S*head,inti)//删除链表中值为i的结点

{

V_sub_S*p1,*p2;

p1=head;

while(i!

=p1->num&&p1->next!

=NULL)

{

p2=p1;

p1=p1->next;

}

p2->next=p1->next;

returnhead;

}

voidDijkstra(V_sub_S*head,ints)

{

structV_sub_S*p;

intmin;

S[0]=s;

for(inti=0;i

{

D[i]=G[s][i];

}

for(inti=1;i

{

p=head->next;

min=p->num;

while(p->next!

=NULL)

{

if(D[p->num]>D[(p->next)->num])

min=(p->next)->num;

p=p->next;

}

S[i]=min;

head=DelMin(head,min);

p=head->next;

while(p!

=NULL)

{

if(D[p->num]>D[min]+G[min][p->num])

{

D[p->num]=D[min]+G[min][p->num];

}

p=p->next;

}

}

}

voidPrint(structV_sub_S*head)

{

structV_sub_S*p;

p=head->next;

while(p!

=NULL)

{

if(D[p->num]!

=MAX)

{

cout<<"D["<num<<"]:

"<num]<

p=p->next;

}

else

{

cout<<"D["<num<<"]:

"<<"∞"<

p=p->next;

}

}

}

intmain()

{

structV_sub_S*head;

cout<<"输入源点s(0到4之间):

";

cin>>s;

head=create();

Dijkstra(head,s);

head=create();

Print(head);

system("pause");

return0;

}

2、分支限界法

#include

#include

usingnamespacestd;

#defineMAX9999

#defineN60

intn,dist[N],a[N][N];

classHeapNode

{

public:

inti,length;

HeapNode(){}

HeapNode(intii,intl)

{

i=ii;

length=l;

}

booloperator<(constHeapNode&node)const

{

returnlength

}

};

voidshorest(intv)

{

priority_queueheap;

HeapNodeenode(v,0);

for(inti=1;i<=n;i++)dist[i]=MAX;

dist[v]=0;

while

(1)

{

for(intj=1;j<=n;j++)

if(a[enode.i][j]

{

dist[j]=enode.length+a[enode.i][j];

HeapNodenode(j,dist[j]);

heap.push(node);

}

if(heap.empty())break;

else

{

enode=heap.top();

heap.pop();

}

}

}

intmain()

{

cin>>n;

for(inti=1;i<=n;i++)

for(intj=1;j<=n;j++)

{

cin>>a[i][j];

if(a[i][j]==-1)a[i][j]=MAX;

}

shorest

(1);

for(inti=2;i

cout<

return0;

}

3、运行结果(截图)

4、计算复杂性分析(时间、空间)

求单源、无负权的最短路。

时效性较好,时间复杂度为O(V*V+E),可以用优先队列进行优化,优化后时间复杂度变为0(v*lgn)。

源点可达的话,O(V*lgV+E*lgV)=>O(E*lgV)。

当是稀疏图的情况时,此时E=V*V/lgV,所以算法的时间复杂度可为O(V^2)。

可以用优先队列进行优化,优化后时间复杂度变为0(v*lgn)。

5、算法改进

Dijkstra算法的运行时间要低,但它要求所有边的权值非负。

Dijkstra算法中设置了一个顶点集合S,从源点s到集合中的顶点的最终最短路径的权值均已确定。

算法反复选择具有最短路径估计的顶点u∈V-S,并将u加入S中,对u的所有出边进行松弛。

由于总是在V-S中选择“最近”的顶点插入集合S中,可以说Dijkstra算法使用了贪心策略。

可以想象,在Dijkstra算法的执行过程中,最短路径估计沿着以s为根的最短路径树向下传播。

由于不存在负权边,最短路径树中的边一旦确定就不再改变。

可以使用最小堆构建的最小优先队列(同Prim算法),存储集合V-S中的顶点。

Dijkstra算法需要|V|次运行时间为O(lgV)的heap_extract_min操作和|E|次运行时间为O(lgV)的heap_decrease_key操作,因此,总的运行时间为O((V+E)lgV),如果所有顶点都从源点可达的话,则为O(ElgV)。

 

成绩单

 

成绩

 

 

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

当前位置:首页 > 考试认证 > 公务员考试

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

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