【算法设计与数据结构】拓扑排序算法的实现——Kahn算法及基于dfs的算法

来源:互联网 发布:淘宝质量问题的定义 编辑:程序博客网 时间:2024/04/29 12:35

    拓扑排序的定义和原理等我不再赘述,各种教材和网络上都有详细解释,今天我主要谈一谈两种实现拓扑排序的算法。

Kahn算法:

<span style="white-space:pre"></span>将所有入度为0的顶点加入队列q;while(!q.empty() ){u = q.front();q.pop();list.push(u);for (u的每个邻接点v){删除边(u, v);if (indegree(v) == 0)q.push(v);}}if (图G还有边存在)return 存在环elsereturn list;
    以上这种是比较典型的求拓扑排序的算法,算法复杂度为O(v+e),常可用来判断该图是否是DAG(有向无环图)
    在算法导论上,介绍的则是另外一种算法,它是基于DFS的,实现十分简单,仅需要在DFS中多加一个语句即可。
    基于DFS的拓扑排序算法(前提:图是DAG):
L ← 用于存放排序结果的数组S ← 出度为0的顶点的集合for (S中的每个顶点)    dfs(n) void dfs(node n){if (!vis[n]){vis[n] = true;for (每一个顶点m,满足m->n)dfs(m);}        L.push(n);}

    可以看到,我们只是在dfs函数快退出时将结点加入到L中而已。

    (注意,for中的顶点m,满足的是m->n而不是n->m)

    下面简单证明一下它的正确性:
    对任意的边m->n,当调用dfs(n)的时候,有如下两种情况:
    1) dfs(m)还没有被调用,此时会调用dfs(m),只有dfs(m)返回之后,dfs(n)才会返回

    2) dfs(m)已经被调用过并返回了

    (由于本图是DAG,所以不存在dfs(m)已经被调用,但是在dfs(n)在被调用时还未返回的情况)

    无论是以上哪一种情况,m都会先于n被添加到L中。所以对于任意边m->n,在L中,m总会在n前面。

    本算法的复杂度为O(v+e),需要注意的一些点是,本算法是建立在图为DAG的基础上的,当然,可以进行一些修改来做环路检测,另外, 本算法的起点是对每个出度为0的顶点进行dfs,而Kahn算法则是从每个入度为0的顶点出发。为何本算法需要从出度为0的顶点出发呢?因为出度为0的顶点必然排在最后面,而最先调用dfs的顶点最后才加入L中。

    对比两种算法,有着异曲同工之妙,一个从入度为0的顶点出发,一个从出度为0的顶点出发;一个对m->n中的每个n进行操作,一个对m->n中的每个m进行操作。

    如果需要判断该图是否为DAG,那么第一种算法是不错的选择,如果已经知道该图为DAG,则第二种算法更加简洁!

0 0
原创粉丝点击