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

图的定义

图(graph)G = (V,E)由顶点(vertex)的集V和边(Edge)的集E组成。有时也把边称作弧(arc),如果点对(v,w)是有序的,那么图就叫做有向的图(有向图)。顶点v和w邻接(adjacent)当且仅当(v,w)属于E。

如果无向图中从每一个顶点到其他每个顶点都存在一条路径,则称该无向图是连通的(connected)。具有这样性质的有向图称为是强连通的(strongly connected)。如果有向图不是强连通的,但它的基础图(underlying graph)(也就是其弧上去掉方向说形成的图)是连通的,那么称该有向图是弱连通的(weakly connected)。完全图(complete graph)是其每一对顶点间都存在一条边的图。

如下表示了一个有着7个顶点和12条边的有向图。

拓扑排序

拓扑排序(topological sort)是对有向无环图的顶点的一种排序,它使得如果存在一条从vi到vj的路径,那么在排序中vj出现在vi的后面。正是由于这个特性,如果图含有回路,那么拓扑排序是不可能的。

求拓扑排序算法的一种简单方式:选中一个没有入边的顶点,显示出该点,并将它和它的边一起从图中删除,然后对图的其余部分应用同样的方法处理。

所谓入度(indegree)是指的顶点v的边(u,v)的条数。假设每一个顶点的入度被存储且图被读入一个邻接表中,下面的代码则可以生成一个拓扑排序。

对上图应用拓扑排序的结果如下:

最短路径算法

单源最短路径问题:给定一个加权图G=(V,E)和一个特定顶点s作为输入,找出从s到G中每一个其他点的最短加权路径。

如下图所示,从v1到v6的最短加权路径的值为6(v1-v4-v7-v6),从v2到v5的最短加权路径的值为5(v2-v4-v5)。

下面这个图从v5到v4的最短加权路径可就有意思了,它是1么?不是。按照v5-v4-v2-v5-v4的路径走则是一条更短的路径了,因为这是带负值回路的图。而由于带负值而引入的循环,这个循环叫做负值回路(negative-cost cycle),当它出现在图中时,最短路径问题就是不确定的了。有负值的边未必不好,但它们明显使问题更加难了。

当未指明所讨论的是加权路径还是无权路径时,如果图是加权的,那么路径就是加权的。

广度优先搜索

请先忽视下图中所有的下标,让我们从头开始。随意选择一个点,此处选择v3,作为切入点。因此到v3的距离为0。从v3出发,距离为1的结点是v1和v6;继续下一步,v6已经无路可走,而与v1距离为1的是v2和v4,因此对它们标记上2;继续下去,v2和v4走一步都可以到v5,v4走一步可以到v7,因此v5和v7被标记为3。至此搜索便结束了。

这就是广度优先搜索(breadth-first search),该方法按层处理顶点。距起始点最近的那些顶点首先被求值,最远点则最后被求值,这很像对树的层序遍历(level-order traversal)。

Dijkstra算法

前面的广度优先搜索中的图是无权图,而如果一旦变成了加权图,那么问题就变得困难起来了。

对于每个顶点,我们标记为known以及unknown,和上面一样,还得有一个距离的dv。与无权最短路径一样,Dijkstra算法也是按阶段进行,在每个阶段选择一个顶点v,它在所有unknown顶点中具有最小的dv,同时算法声明从s到v的最短路径是known的。然后紧接着,不断的进行下去即可。

那么这个算法到底是怎么回事了?请看下图。

图中已经对权重做好了标记,以v1作为切入点,因此初始情况如下左图。

v1此时已经是known的了,而其有2个邻接点v2和v4,因此可以调整为如下右图。正无穷图标标识没有连通。pv表示前一个邻接点。

毫无疑问这里会接下来走到v4去,因为v4的权重为1比v2的权重为2要小。调整为如下左图。

可能你已经看到了上图中的右图而好奇为什么下一步是v2,但是v4根本不能走到v2。因为v4能够走到的,比如v3,权重从v1开始一共是3,这比从v1到v2还要大。于是就跳转回到了v2。

下一步便走到了v5,因为只有值为3的权重,同样的v3也是,于是它们俩被双双标记为known。如下左图所示。

紧接着走到了v7,同时v6下调到了5+1=6得到了如下右图。至于为什么要做这个调整,是因为此时v1到v7的加权为1+4=5,而v7到v6的加权为1,所以就有了这个调整。

最后便顺势走到了v6完成了整个Dijkstra算法,它们都已被标记为known。

在后面还将会有一种斐波那契堆,针对Dijkstra算法做了优化,欢迎大家的继续关注。

具有负边值得图

而如果一个图具有负的边值,那么Dijkstra算法就行不通了。这是因为一个顶点u被声明为known后,那就可能从某个另外的unknown顶点v有一条回到u的负的路径。而“回到”就意味着循环,前面的例子中我们已经知道了循环是多么的……

问题并非没有解决的办法,如果我们有一个常数X,将其加到每一条边的值上,这样除去负的边,再计算新图的最短路径,最后把结果应用到原图上。然后这个解决方案也是布满了荆棘,因为居多许多条边的路径变得比那些具有很少边的路径权重更重了。如果我们将s放到队列中,然后再每一个阶段让一个顶点v出队,找出所有与v邻接的顶点w,使得dw>dv+cvw,然后更新到dw和pw,并在w不在队列中时将它放到队列中,可以为每一个顶点设置一个位(bit)以指示它在队列中出现的情况。

无环图

如果图是无环的,则可以通过改变声明顶点为known的顺序,或者叫做顶点选取法则来改进Dijkstra算法。这种方法通过拓扑排序来选择顶点,由于选择和更新可以在拓扑排序执行的过程中执行,因此新的算法只需要一趟就可以完成。

通过下面这个动作结点图(activity-node graph)来解释什么是关键路径分析(critical path analysis)再合适不过了。一条边(v,w)表示动作v必须在动作w开始前完成,如前面说描述的那样,这就意味着图必须是无环的。

为了进行这些运算,我们把动作结点图转化成事件结点图(event-node graph),每个事件对应于一个动作和所有与它相关的动作完成。

所以现在我们需要找出事件的最早完成时间,只要找出从第一个事件到最后一关事件的最长路径的长。因为有正值回路(positive-cost cycle)的存在最长路径问题常常是没有意义的。而由于事件结点图是无环图,那就不需要担心回路的问题了,这样一来就不用有所顾忌了。

以下是最早完成时间。

以下是最晚完成时间。

借助顶点的拓扑排序计算最早完成时间,而最晚完成时间则通过倒转拓扑排序来计算。

而事件结点图中每条边的松弛时间(slack time)代表对应动作可以被延迟而不推迟整体完成的时间量,最早完成时间、最晚完成时间和松弛时间如下所示。

某些动作的松弛时间为0,这些动作是关键性的动作,它们必须按计划结束。至少存在一条完成零-松弛边组成的路径,这样的路径是关键路径(critical path)。



感谢大家的支持,欢迎大家的关注或收藏。



为使本文得到斧正和提问,转载请注明出处:

http://blog.csdn.net/nomasp

时间: 2024-10-15 06:04:19

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

拓扑排序--是否为有向无环图

步骤: 1.定义一个队列Q,并把所有入度为0的结点加入 2.取队首输出,然后删去所有从它出发的边,并令这些边到达顶点的入度-1,如果某个顶点的入度减为0则将其放入队列 3.重复2直到队列为空.如果队列未空时结点数目恰为N,说明拓扑排序成功---有向无环图 vector<int> G[MAX];//邻接表 int n,m,inDegree[MAX];//入度 bool topologicalSort(){ int num=0; queue<int> Q; for(int i=0;i&

数据结构与算法——有向无环图的拓扑排序C++实现

拓扑排序简介: 拓扑排序是对有向无环图的顶点的一种排序,它使得如果存在一条从Vi到Vj的路径,那么在排序中Vi在Vj的前面. 如果图中含有回路,那么拓扑排序是不可能的.此外,拓扑排序不必是唯一的,任何合理的排序都可以. 对于上面的无环图:v1,v2,v5,v4,v3,v7,v6和v1,v2,v5,v4,v7,v3,v6都是合理的拓扑排序. 一个简单的求拓扑排序的思路: 1.先找出任意一个没有入边的顶点 2.然后显出该点,并将它和它邻接的所有的边全部删除. 3.然后,对图中其它部分做同样的处理.

47. 蛤蟆的数据结构笔记之四十七的有向无环图的应用排序

47.蛤蟆的数据结构笔记之四十七的有向无环图的应用排序 本篇名言:"君子喻于义 ,小人喻于利. -- 孔丘" 接下去来看下有向无环图. 欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron/article/details/47110397 1.  有向无环图 一个无环的有向图称做有向无环图(directedacycline praph).简称DAG图.DAG 图是一类较有向树更一般的特殊有向图,图1给出了有向树.DAG图和有向图 有向无环图是描述含有公共

有向无环图(DAG)拓扑排序的两种方法

如下图的DAG: 第一种: (1)从AOV网中选择一个没有前驱的顶点并且输出它: (2)从AOV网中删除该顶点,并且上去所有该顶点为尾的弧: (3)重复上述两步,直到全部顶点都被输出,或者AOV网中不存在没有前驱的顶点. 第二种: 使用深度优先搜索(DFS),并标记每一个节点的第一次访问(pre)和最后一次访问时间(post),最后post的逆序就是DAG的拓扑排序,其实也是节点在进行DFS搜索时,出栈的逆序就是拓扑排序. 拓扑序列的结果有: (1) c++,高等数学,离散数学,数据结构,概率论

[从今天开始修炼数据结构]无环图的应用 —— 拓扑排序和关键路径算法

上一篇文章我们学习了最短路径的两个算法.它们是有环图的应用.下面我们来谈谈无环图的应用. 一.拓扑排序 博主大学学的是土木工程,在老本行,施工时很关键的节约人力时间成本的一项就是流水施工,钢筋没绑完,浇筑水泥的那帮兄弟就得在那等着,所以安排好流水施工,让工作周期能很好地衔接就很关键.这样的工程活动,抽象成图的话是无环的有向图. 在表示工程的有向图中,用顶点表示活动,弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,成为AOV网(Active On Vertex Network) ※ 若在

javascript实现有向无环图中任意两点最短路径的dijistra算法

有向无环图 一个无环的有向图称做有向无环图(directed acycline praph).简称DAG 图.DAG 图是一类较有向树更一般的特殊有向图, dijistra算法 摘自 http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html 1.定义概览 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Dijk

最短路径算法-Dijkstra算法的应用之单词转换(词梯问题)

一,问题描述 在英文单词表中,有一些单词非常相似,它们可以通过只变换一个字符而得到另一个单词.比如:hive-->five:wine-->line:line-->nine:nine-->mine..... 那么,就存在这样一个问题:给定一个单词作为起始单词(相当于图的源点),给定另一个单词作为终点,求从起点单词经过的最少变换(每次变换只会变换一个字符),变成终点单词. 这个问题,其实就是最短路径问题. 由于最短路径问题中,求解源点到终点的最短路径与求解源点到图中所有顶点的最短路径复

最短路径之Dijkstra算法

Dijkstra算法: 首先,引进一个辅助向量D,它的每个分量D[i]表示当前所找到的从始点v到每个终点vi的的长度:如D[3]=2表示从始点v到终点3的路径相对最小长度为2.这里强调相对就是说在算法过程中D的值是在不断逼近最终结果但在过程中不一定就等于长度.它的初始状态为:若从v到vi有弧,则D为弧上的权值:否则置D为∞.显然,长度为 D[j]=Min{D | vi∈V} 的路径就是从v出发的长度最短的一条.此路径为(v,vj). 那么,下一条长度次短的是哪一条呢?假设该次短路径的终点是vk,

图的单源最短路径:Dijkstra算法实现

本文介绍的是图的非负权值的单源最短路径问题.问题的提出是,对于有权图D,t提供源点v,要找到从v到其他所有点的最短路径,即单源最短路径问题,在本文中,解决这一问题,是普遍比较熟悉的Dijkstra算法. 算法核心思想参见维基.简而言之,设集合S存放已经求出了最短路径的点.初始状态S中只有一个点v0,之后每求得v0到vn的最短路径,就会更新v0到所有vn邻接的点的一致的最短路径(不一定是最终的最短路径),如此重复,每次会确定v0到一个点的最短路径,确定好的点加入S中,直至所有点进入S结束.在本文中