图论之拓扑排序

来源:互联网 发布:游族网络最新消息 编辑:程序博客网 时间:2024/06/03 01:56

一、定义

将有有向无环图(GAD)中的顶点以线性方式进行排序。即对于任何连接自顶点u到顶点v的有向边uv,在最后的排序结果中,顶点u总是在顶点v的前面。u→v,u在前,v在后。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。

举个栗子,最简单的选课问题。

比如我大二想学数据结构,那我就必须在大一C语言不挂科,C语言是数据结构的基础课程。那么这个制定选修课程顺序的过程,实际上就是一个拓扑排序的过程,每门课程相当于有向图中的一个顶点,而连接顶点之间的有向边就是课程学习的先后关系将这个过程以算法的形式描述出来的结果,就是拓扑排序。

但是若说学C语言之前必须学完数据结构,我们就会哭哭啊,因为不会啊到底哪门是基础啊,所以这样的顺序就不成立了。这是相互依赖的关系了,没有谁先谁后了,在有向图中以环呈现。

所以,一个有向图能被拓扑排序的充要条件就是它是一个有向无环图。

二、偏序和全序 

偏序和全序实际上是离散数学中的概念。

还是以上面选课的例子来描述这两个概念。假设我们在学习完了C语言这门课后,可以数据结构或者数字电路。这个或者表示,学习数据结构和数字电路这两门课之间没有特定的先后顺序。因此,在我们所有可以选择的课程中,任意两门课程之间的关系要么是确定的(即拥有先后关系),要么是不确定的(即没有先后关系),绝对不存在互相矛盾的关系(即环路),不然到底是上课还是不上啊哭哭以上就是偏序的意义,抽象而言,有向图中两个顶点之间不存在环路,至于连通与否,是无所谓的。所以,有向无环图必然是满足偏序关系的。

理解了偏序的概念,那么全序就好办了。所谓全序,就是在偏序的基础之上,有向无环图中的任意一对顶点还需要有明确的关系反映在图中,就是单向连通的关系,注意不能双向连通,那就成环了可见,全序就是偏序的一种特殊情况。回到我们的选课例子中,如果数据结构需要在学习了c语言之后才能学习(因为C语言是算法基础啊!没有语言哪来的算法实现!),那么它们之间也就存在了确定的先后顺序,原本的偏序关系就变成了全序关系。

实际上,很多地方都存在偏序和全序的概念。

比如对若干互不相等的整数进行排序,最后总是能够得到唯一的排序结果(从小到大,下同)但是如果我们以偏序/全序的角度来考虑一下这个再自然不过的问题,可能就会有别的体会了。

 那么如何用偏序/全序来解释排序结果的唯一性呢?

我们知道不同整数之间的大小关系是确定的,即1总是小于4这就是说,这个序列是满足全序关系的。而对于拥有全序关系的结构(如拥有不同整数的数组),在其线性化(排序)之后的结果必然是唯一的。对于排序的算法,我们评价指标之一是看该排序算法是否稳定,即值相同的元素的排序结果是否和出现的顺序一致。比如,我们说快速排序是不稳定的,这是因为最后的快排结果中相同元素的出现顺序和排序前不一致了。如果用偏序的概念可以这样解释这一现象:相同值的元素之间的关系是无法确定的。因此它们在最终的结果中的出现顺序可以是任意的。而对于诸如插入排序这种稳定性排序,它们对于值相同的元素,还有一个潜在的比较方式,即比较它们的出现顺序,出现靠前的元素大于出现后出现的元素。因此通过这一潜在的比较,将偏序关系转换为了全序关系,从而保证了结果的唯一性。

拓展到拓扑排序中,结果具有唯一性的条件也是其所有顶点之间都具有全序关系。如果没有这一层全序关系,那么拓扑排序的结果也就不是唯一的了。在后面会谈到,如果拓扑排序的结果唯一,那么该拓扑排序的结果同时也代表了一条哈密顿路径

三、拓扑排序的三种方法

整体思路:先找出任意一个没有入边的顶点,然后显示出该顶点,并将它和它的边一起从图中删除。然后,对图的其余部分用同样的方法处理。


*度: 图中每个顶点的度为以该顶点为一端点的边的数目。记为 D(V) 。
*入度和出度:对于有向图,入度为以该顶点为终点的边的数目,出度为以该顶点为起点的边的数目。

1.无前趋的的顶点优先拓扑排序

    思路:在有向图建立完成之后,维护两个点集,一个是当前出度为0的点集,记为①,另一个是出度不为0 的点集,记为②,以及一个记录各个点出度的数组。首先遍历一遍图的全部边,初始化所有点的出度,然后出度为0的点依次 入①,然后将①中的点分别出列,每次出列都需要更新各个点的出度,即把所有跟出列的点邻接的点出度-1(有多条边,则相应减掉边数,一般简单图不会有多重边),直至①变成空集。这个时候,如果②也变成了空集,证明排序成功,否则,原图不存在拓扑排序(图中有环)。最终的排序结果就是从①中出列的点的逆序


2.无后继的的顶点优先拓扑排序

  思路:跟1的方法类似,不过这次是维护根据点的入度进行统计。在有向图建立完成之后,维护两个点集,一个是当前入度为0的点集,记为①,另一个是入度不为0 的点集,记为②,以及一个记录各个点入度的数组。首先遍历一遍图的全部边,初始化所有点的入度,然后入度为0的点依次 入①,然后将①中的点分别出列,每次出列都需要更新各个点的入度,即把所有跟出列的点邻接的点入度-1(有多条边,则相应减掉边数,一般简单图不会有多重边),直至①变成空集。这个时候,如果②也变成了空集,证明排序成功,否则,原图不存在拓扑排序(图中有环)。最终的排序结果就是从①中出列的点的顺序


3.基于DFS递归的拓扑排序

  思路:从图的起点开始进行深度优先搜索,在搜索过程中,把没有后继(相当于出度为0)的点出列(这个过程中,已经出列的点不算是它的前继点,相当于删除了该点),点的出列顺序就是拓扑排序结果的逆序

ps:我们一般习惯用第三种吧,还在摸索中XD



0 0
原创粉丝点击