数据结构与算法分析(五)——最短路径算法

  • 0) 引论

正如名字所言,最短路径算法就是为了找到一个图中,某一个点到其他点的最短路径或者是距离。

最短路径算法一般分为四种情况:

a) 无权重的最短路径

b) 有权重的最短路径

c) 边的权重为负的图

d) 无圈的图

ps:上面的情况针对的都是有向图。

  • 1) 无权重的最短路径

下图是一个例子:假设我们取点v3作为初始点,计算点v3到图中所有点的路径以及距离(包括点v3)。

a) v3到v3的路径长为0。

b) 沿着v3的邻接点查找,找到v1,那么v3到v1的路径长为1;找到v6,那么v3到v6的路径长为1。

c) v3的邻接点已经找完了,接下来找v1,v6的邻接点。

d) v1的邻接点为v2,v4,它们对应的路经长为2;v6无邻接点,v6部分结束。

e) v2的邻接点为v4,v5,而v4已经查找过了,舍弃;v2到v5的路径长为3;v4的邻接点为v5,v7,而v5已经查找过了,舍弃;v4到v7的路径长为3。

f) 至此,结束。

对于这一步骤,可以用一个表格来表示,以便于编程理解:

表中有3个表示状态的变量:

a) dv表示点s到图中每个点的距离,结合上面的例子,s=v3.

b) pv表示图中点的路径前继点。

c) Known表示点是否被处理,处理了,则标记为1 。

伪代码:

 1 void Unweighted(Table T)
 2 {
 3     int CurrDist;
 4     Vertex V,W;
 5     for(CurrDist=0;CurrDist<NumVertex;CurrDist++)
 6     {
 7         for each vertex V
 8             if(!T[V].Known&&T[V].Dist==CurrDist)
 9             {
10                 T[V].Known = True;
11                 for each W adjacent to V
12                     if(T[W].Dist == Infinity)
13                     {
14                         T[W].Dist = CurrDist+1;
15                         T[W].Path = V;
16                     }
17             }
18     }
19 }  

通拓扑排序一样,对于上面的程序也是存在改进的地方的(第07行),我们没有必要去对each vertex V做一下这么多的步骤,只需要对相邻接的点做就好了。因此用一个队列来使每一个处理的点入队,然后处理其邻接的点。

 1 void Unweighted(Table T)
 2 {
 3     Queue Q;
 4     Vertex V,W;
 5
 6     Q = CreateQueue(NumVertex);
 7     MakeEmpty(Q);
 8     Enqueue(S,Q);//S is the start Vertex
 9
10     while(!IsEmpty(Q))
11     {
12         V = Dequeue(Q);
13         T[V].Known=True;
14         for each W adjacent to V
15             if(T[W].Dist == Infinity)
16             {
17                 T[W].Dist = T[V].Dist+1;
18                 T[W].Path = V;
19                 Enqueue(W,Q);
20             }
21     }
22     Free(Q);
23 }  

  • 2) 有权重的最短路径

有权重的最短路径算法,称之为Dijkstra‘s algorithm。相对于无权重的最短路径算法,有权重的最短路径算法会显得难一点,这是因为权重的引入会使某些路径的加权重长度发生颠倒。

Dijkstra‘s algorithm是一种greedy algorithm。贪婪算法就是在算法的每一个阶段都取最大值。一般情况下,贪婪算法能取得较好的效果。但是贪婪算法是有其缺陷的,也就是说能达到局部最优,而未必能达到全局最优,举个例子,假设要兑换15分的硬币,而硬币有12分的,10分的,5分的,1分的,那么根据贪婪算法,最终的兑换结果为1个12分的,3个一分的;而最优结果应该是一个10分的,一个5分的。(这里我们定义最优为硬币个数最少)。

Dijkstra‘s algorithm的步骤是:

对于每一个阶段,Dijkstra‘s algorithm会在所有未处理的点中选取一个最小距离的点v,然后把给定点s到v的路径定义为最小路径。

下面是一个例子:

对应的表的形式表示:

下面是具体的实现说明:

a) 最初的点s=v1;计算s到图中所有点的最短路径。

b) 找到v1邻接的点v2,v4,分别标出s到v2,v4的路径长,同时对v1标注T[v1].Known=1。

c) 由于v4路径长较小,因此选择v4,找到v4邻接的点v3,v5,v6,v7,标注出他们的路径长;同时对v1标注T[v4].Known=1。

d) 现有的所有未处理路径中v2最短,因此处理v2,找到v2邻接的点v4,v5; v2到v4,v5的路径长远大于v4,v5已有的路径长,因此不更新。同时T[v2].Known=1。

e) 现有的所有未处理路径中v3,v5最短,处理v3,邻接点为v1,v6,路径分别为7,8;对于v3到v6的距离,因为8<9,因此需要更新;对于v3到v1的距离不需要更新。同时T[v3].Known=1。

f)  对于v5,邻接点为v7,v5到v7的路径长为 3+6>5,因此不更新;同时T[v5].Known=1。

g) 现在处理v7,v7的邻接点为v6,v7到v6的路径长为5+1<8,因此需要更新;同时T[v7].Known=1。

h) 现在处理v6,v6没有邻接点了,因此可以结束了。同时T[v6].Known=1。

伪代码实现:

 1 typedef int Vertex;
 2 typedef int DistType;
 3
 4 struct TableEntry
 5 {
 6     List Header;
 7     int Known;
 8     DistType Dist;
 9     Vertex Path;
10 }
11
12 #define NotAVertex (-1)
13 typedef struct TableEntry Table[NumVertex];
14
15 void InitTable(Vertex Start,Graph G,Table T)
16 {
17     int i;
18     ReadGraph(G,T);
19     for(i=0;i<NumVertex;i++)
20     {
21         T[i].Known = 0;
22         T[i].Dist = Infinity;
23         T[i].Path = NotAVertex;
24     }
25     T[Start].Dist = 0;
26 }
27
28 void Dijkstra(Table T)
29 {
30     Vertex V,W;
31     for(;;)
32     {
33         V = FindSmallestUnknownDistanceVertex();
34         if(V==NotAVertex)
35             break;
36         T[V].Known = Ture;
37         for each W adjacent to V
38             if(!T[V].Known)
39                 if(T[V].Dist+Cvw<T[W].Dist)
40                 {
41                     Decrease(T[W].Dist to T[V].Dist+Cvw);
42                     T[W].Path = V;
43                 }
44     }
45 }  

相对于无权重的最短路径,这里权重的更新为Dist = Dist + Cvw;

  • 3) 边的权重为负的图

边的权重为负,这将是一个比较糟糕的事情,这无法利用上面的两种方法处理,因为只要循环负边,则路径可以无限变小,如下图所示:

点1到点6的最短路径是2么?当然不是,我们没法求出其最短路径,因为我们可以沿着路径1,6,5,7,6这样一次下来,路径为-3,由于6,5,7,6是一个圈,我们可以一直循环下去且路径为负值,因此无法确定点1到点6的最短路径。

那么遇到这种问题该怎么解决呢?

a) 给所有权重加上一个正值,是所有权重均为非负,上面的例子中,所有权重加上11,则就可以应用Dijkstra方法处理了。

  • 4) 无圈的图

对于无圈图,也可以应用Dijkstra方法处理,无圈图也可以看做是上面的一种特例。对于无圈图,可以利用拓扑排序的顺序选择起始点,权重的更新也可以按照拓扑排序的顺序进行,因此算法可以一次进行,这个可以看做是对Dijkstra方法的改进,并且是向简单方向的改进。

无圈图可以模拟像下坡滑雪等问题,一直需要沿着向下的方向,不应该有圈。

而无圈图的最大应用不在这里,而是一个被称之为关键路径分析法的应用。

如下图所示:

这幅图表示的意思是要想做D中的事情,必须先做完A和B,而A,B则是可以并行的。因此这个图中的问题不是寻找最短路径,而是找到并行完成图中的所有点所需要的最短时间,以及最晚的时间。

解决这个问题需要把上面的动作节点图转换为下面的事件节点图

对于事件节点图,只需要找出第一个事件到最后一个事件的最长路径的长就好了。

假设ECi表示节点i的最早完成时间,那么利用下面的法则

最早完成时间结果为:

转自:http://blog.csdn.net/changyuanchn/article/details/17077379

时间: 2024-08-30 02:15:01

数据结构与算法分析(五)——最短路径算法的相关文章

《数据结构与算法分析:C语言描述》复习——第九章“图论”——无权值的最短路径问题

2014.07.04 18:24 简介: 给定一个有向图,你可以认为每条边长度都是1(所以叫无权值).下面的算法可以求出从特定的起点到终点的最短路径长度. 描述: 从起点出发,根据当前顶点出发的边进行广度优先搜索,直至找到终点即可.如果搜索结束了仍然没有找到终点,那么起点无法到达终点. 实现: 1 // A simple illustration for unweighted shortest path. Graph represented by adjacency matrix. 2 #inc

《数据结构与算法分析:C语言描述》复习——第五章“堆”——二叉堆

2014.06.15 22:14 简介: 堆是一种非常实用的数据结构,其中以二叉堆最为常用.二叉堆可以看作一棵完全二叉树,每个节点的键值都大于(小于)其子节点,但左右孩子之间不需要有序.我们关心的通常只有堆顶的元素,而整个堆则被封装起来,保存在一个数组中. 图示: 下图是一个最大堆: 实现: 优先队列是STL中最常用的工具之一,许多算法的优化都要利用堆,使用的工具就是优先队列.STL中的优先队列通过仿函数来定义比较算法,此处我偷懒用了“<”运算符.关于使用仿函数的好处,我之后如果有时间深入学习S

数据结构:单源最短路径--Dijkstra算法

Dijkstra算法 单源最短路径 给定一带权图,图中每条边的权值是非负的,代表着两顶点之间的距离.指定图中的一顶点为源点,找出源点到其它顶点的最短路径和其长度的问题,即是单源最短路径问题. Dijkstra算法 求解单源最短路径问题的常用方法是Dijkstra(迪杰斯特拉)算法.该算法使用的是贪心策略:每次都找出剩余顶点中与源点距离最近的一个顶点. 算法思想 带权图G=<V,E>,令S为已确定了最短路径顶点的集合,则可用V-S表示剩余未确定最短路径顶点的集合.假设V0是源点,则初始 S={V

《数据结构与算法分析:C语言描述》复习——第十章“算法设计技巧”——Alpha-Beta剪枝

2014.07.08 22:43 简介: “搜索”与“剪枝”几乎是如影随形的.此处的“搜索”指的是带有回溯算法的深度优先搜索. 在之前的“Minimax策略”中我们给出了一个三连棋的程序,运行后你就知道计算一步棋要花多少时间. 为了计算最优的一步棋,我们可能需要递归9万多次.如果毫无疑问这种阶乘式的穷举过程必须通过剪枝来加速. 本篇介绍一种用于Minimax策略的剪枝思路——α-β剪枝. 剪枝的英语是pruning,所以不要想当然说成trimming. 图示: 在上一篇讲解Minimax策略的博

43. 蛤蟆的数据结构笔记之四十三最短路径之迪杰斯特拉(Dijkstra )算法

43. 蛤蟆的数据结构笔记之四十三最短路径之迪杰斯特拉(Dijkstra )算法 本篇名言:"辛勤的蜜蜂永没有时间悲哀.--布莱克" 这次来看下Dijkstra )算法.还是老方法,先原理,后实现.代码来自网络. 欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron/article/details/47046031 1.  最短路径 最短路径问题是图论研究中的一个经典算法问题, 旨在寻找图(由结点和路径组成的)中两结点之间的最短路径. 管道铺设.线路安排

《数据结构与算法分析》学习笔记(五)&mdash;&mdash;队ADT

一.队的概念        队列也是一种表,但是是一种受限的表,只允许从一端插入,另一端山粗的表. 二.队列的数组实现 #define QMAXSIZE 100 typedef int Position; typedef int QElement; typedef struct queue { QElement Els[QMAXSIZE]; Position head,tail; }Queue; void QCreate(Queue &Q) { Q.head = Q.tail = 0; } vo

数据结构与算法分析之简单排序算法

在排序算法中,简单排序主要有三种,分别为冒泡排序.选择排序.插入排序,学习理解好这三种排序算法有助于进一步研究数据结构与算法分析.下面,简单地谈一谈冒泡排序.选择排序.插入排序的原理及区别. 冒泡排序原理: 1.比较相邻的元素.如果前一个比后一个大,它们就交换. 2.每对元素都要进行同样的动作,从后往前比较. 3.每趟都会确定一个位置的元素,因此n个元素,需要n-1趟才能确定各个元素的位置. 例如:对23,4,56,11四个数进行冒泡排序. 第一趟 4,23,11,56 第二趟 4,11,23,

【数据结构】拓扑排序、最短路径算法、Dijkstra算法、无环图等等

图的定义 图(graph)G = (V,E)由顶点(vertex)的集V和边(Edge)的集E组成.有时也把边称作弧(arc),如果点对(v,w)是有序的,那么图就叫做有向的图(有向图).顶点v和w邻接(adjacent)当且仅当(v,w)属于E. 如果无向图中从每一个顶点到其他每个顶点都存在一条路径,则称该无向图是连通的(connected).具有这样性质的有向图称为是强连通的(strongly connected).如果有向图不是强连通的,但它的基础图(underlying graph)(也

看数据结构写代码(47)迪杰斯特拉最短路径算法

这个算法的 思想 根 求 最小生成树算法 普里姆(Prim)算法 极其相似.迪杰斯算法 是求 一个顶点 到其他 顶点的 最短路径算法. 下面 上代码:(用的是 邻接矩阵 表示法) //迪杰斯特拉 最短路径. //从 vex顶点 到其他 顶点的 最短路径 void shortestPath_Dij(MGraph g,char vex){ int loc = graphLocation(g,vex); int minArray[MAX_VERTEX_NUM]={0};//最小路径值 bool fin