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,拓扑排序可以用来为头文件进行排序!



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值