6.2 拓扑排序

拓扑排序

在许多应用中,有向无回路图可用于抽象具有发生先后顺序的事件,图的搜索算法可以用于解决具有先决条件的问题。假设我们要安排一系列任务,但是只有在某个任务的先决条件具备时才能着手完成这个任务。我们希望以某种先后顺序组织这些任务,以便每项任务都是在先决条件已完成的前提下逐个完成。

因为任务之间存在先决条件限制,也就是顶点之间存在方向性,所以这一类问题可以用有向无环图(DAG)来描述。如图给出一个学科学习的例子,其中必须先学完某些功课才能学习其它功课,当然也有一些比较独立的功课,例如体育课。图中有向边(u, v)表示功课u必须在功课v之前学习。所以该图的拓扑排序将可以给出一个功课学习的先后顺序。

图 功课学习先后顺序图

事实上拓扑排序是有向无环图深度优先搜索的直接应用。假设对某一有向无回路图G=(V, E)运行DFS,并确定其所有顶点的搜索完成时间f。图中某一条边(u, v) 在DFS过程中,f[u]必定大于f[v]。所以只要将图中各顶点按照DFS搜索所获得的搜索完成时间由大到小进行排序即可完成拓扑排序。

根据上面的分析,拓扑排序算法实现如下:

    /**
         * 图的拓扑排序算法,排序后各顶点的标号按顺序排放至
         * 数组,并将该数组返回。
         * 
@param g    带排序的有向无环图
         * 
@return    排序后顶点存放至数组
         
*/
        public static int[] topologocal_sort(GraphLnk g){
            // 首先对图进行深度优先搜索
            GraphSearch.DFS(g);
            int n = g.get_nv();
            int[] f = new int[n];
            // 各顶点深度优先搜索时的第二个时间
            for(int i = 0; i < n; i++){
                f[i] = GraphSearch.f[i];
            }
            // 创建数组,用于放置排序后各顶点的标号
            int r[] = new int[n];
            // 按照f值由大到小将各顶点排序
            
// 遍历n次
            for(int i = 0; i < n; i++){
                int _max = f[0], _max_idx = 0;
                // 每次遍历寻找f值最大的下标
                for(int j = 1; j < n; j++)
                    if(f[j] > _max){
                        _max = f[j];
                        _max_idx = j;
                    }
                // 将找到的最大值重赋值为无穷小
                f[_max_idx] = -Integer.MAX_VALUE;
                // 将序号存放置数组r
                r[i] = _max_idx;
            }
            return r;
        }

算法首先对图进行DFS搜索,并获得各顶点完成遍历的时间f。然后,将各顶点的序号逐个取出并放置到待返回的数组中。顶点取出的顺序根据顶点的f时间值由大到小决定。

各顶点标号如图,顶点经过DFS后各顶点的搜索完成时间d[u]/f[u]为:

0)1/14

1)15/16

2)17/18

3)2/7

4)8/13

5)9/12

6)3/6

7)10/11

8)4/5

图 功课学习先后顺序对应的有向无环图

调用拓扑排序函数对该图进行拓扑排序:

    // 图顶点标号对应数组中的功课名称
        String course[] = {"语文", "算术", "体育","英语", 
                        "数学", "基础物理","基础物理英文",
                        "固态物理", "固态物理英文"};
        // 从文件获取图的信息
        GraphLnk GL = 
            Utilities.BulidGraphLnkFromFile("Graph\\graph5.txt");
        // 对图进行拓扑排序
        int od[] = TopologicalSort.topologocal_sort(GL);
        // 打印拓扑排序后顶点顺序
        for(int i = 0; i < od.length; i++){
            System.out.print(od[i] + ". " + course[od[i]]+"  ");
        }

排序后各顶点的顺序为:

2. 体育  1. 算术  0. 语文  4. 数学  5. 基础物理  7. 固态物理  3. 英语  6. 基础物理英文  8. 固态物理英文  

按照这个顺序可以将图中每个顶点沿水平方向形成一个顶点序列,使得图中有向边都由左指向右。

 图中功课学习拓扑排序结果图

后记

   拓扑排序有一个很重要的应用:在c/c++语言中,我们的工程中常常会包含很多头文件,头文件有会include别的头文件,所以这些头文件之间形成一个有向图。编译器在编译时需要确定文件的编译先后次序。aha,拓扑排序可以用来为头文件进行排序!

原文地址:https://www.cnblogs.com/luweiseu/p/2591339.html