Topplogical-Sort

来源:互联网 发布:金越软件 编辑:程序博客网 时间:2024/06/15 04:12

Topological Sort

程度★ 难度★★

楔子

在枚举所有排列的问题之中,如果我们另外再限制谁要排在谁前方、谁要排在谁后方,那么在这些限制之下,合理的排列还会剩下哪些呢?

【注:枚举所有排列,读者们可另行参考「Enumerate all n-tuples」一文。

先后限制与图

谁要排在谁前方、谁要排在谁后方,其实就是两两之间的关系,故可以改用图来表示:把图上一条由A 点连向B 点的边,想成是A 必须排在B 前方( B 必须排在A 后方)。

当然啦,也可以把图上一条由A 点连向B 点的边,想成是A 必须排在B 后方。不过一般来说我们习惯成自然地使用前者。

Topological Sort 与Topological Order

「拓朴排序」是排序一张有向图的点的方式。把图上一条由A 点连向B 点的边,想成是A 必须排在B 前方( B 必须排在A 后方)。Topological Sort 用来找出合理的排列顺序,让每一个点的先后顺序,满足每一条边所规定的先后顺序。

「拓朴顺序」是指一张有向图经过「拓朴排序」后,每一个点的先后顺序。

一张图经过Topological Sort 的结果可以有很多种。只要不违背图上每一条边的先后规定,要怎么排列图上的点都行。

图上不能有环

当图上有环时,便无法进行Topological Sort 。因为环上每一个点都会有连向自己的边,意味着环上每一个点必须排在其他点的后方,环上每一个点都不能在排列顺序中拔得头筹,所以合理的排列顺序不存在。

Topological Sort: 
很普通的演算法

程度★ 难度★

观察问题

要找出合理的排列顺序,首先得决定第一点!知道如何找出第一点,那么就可以循序渐进的再找出第二点、第三点了。

可以作为第一点的点,想必它不必排在其他点后方。也就是说,没有被任何边连向的点,就可以作为第一点。如果有很多个第一点,那么找哪一点都行。

决定第一点之后,那么剩下所有点都会在第一点后方。也就是说,由第一点连出去的边,其先后规定已经被满足了,规定存不存在都无所谓。因此,决定第一点之后,就可以删去此点,以及删去由此点连出去的边──原问题可以递回地缩小!

只要反覆的寻找没有被任何边连向的点,然后删去此点以及删去由此点连出去的边,就可以找出一个合理的排列顺序了。

附带一提,要找出合理的排列顺序,也可以由最后一点开始决定!无论要从第一点找到最后一点,或是从最后一点找到第一点,都是可以的。各位可以想想看该怎么做。

找出一个合理的排列顺序( adjacency matrix )

尽管这个问题有Recursive 的性质,可以用递回实作,但由于递回的分支只有一条,故亦可以用回圈实做。我想大家都会选择以比较简单的回圈方式来实做吧?

实作时可以利用变数纪录图上每一个点目前仍被多少条边连到。寻找没有被任何边连向的点,就直接看该变数是不是零;删去由此点连出去的边,就顺便更新变数的值。

 
  1. bool  adj ][ ];  // adjacency matrix
  2. int  ref ];      //纪录图上每一个点目前仍被多少条边连到
  3.  
  4. void  topological_sort ()
  5. {
  6.     for  ( int  ;  ; ++ )  ref ] =  ;  //初始化为0
  7.  
  8.     // 累计图上每一个点被几条边连到
  9.     for  ( int  ;  ; ++ )
  10.         for  ( int  ;  ; ++ )
  11.             if  ( adj ][ ])
  12.                 ref ]++;
  13.  
  14.     // 开始找出一个合理的排列顺序
  15.     for  ( int  ;  ; ++ )
  16.     {
  17.         // 寻找没有被任何边连向的点
  18.         int   =  ;
  19.         while  (  <   &&  ref ] !=  ) ++ s;
  20.  
  21.         if  (  ==  )  break ;   //找不到。表示目前残存的图是个环。
  22.         ref ] = - ;         //设为已找过(删去s点)
  23.  
  24.         cout  <<  ;           //印出合理的排列顺序的第i点
  25.  
  26.         // 更新ref的值(删去由s点连出去的边)
  27.         for  ( int  ;  ; ++ )
  28.             if  ( adj ][ ])
  29.                 ref ]--;
  30.     }
  31. }

找出一个合理的排列顺序( adjacency lists )

 
  1. int  adj ][ ],  size ];  // adjacency lists
  2. int  ref ];      //纪录图上每一个点目前仍被多少条边连到
  3.  
  4. void  topological_sort ()
  5. {
  6.     for  ( int  ;  ; ++ )  ref ] =  ;  //初始化为0
  7.  
  8.     // 累计图上每一个点被几条边连到
  9.     for  ( int  ;  ; ++ )
  10.         for  ( int  ;  size ]; ++ )
  11.             ref adj ][ ]]++;
  12.  
  13.     // 宣告一个queue来纪录已经没有被任何边连向的点
  14.     queue int >  ;
  15.     for  ( int  ;  ; ++ )
  16.         if  ( ref ] ==  )
  17.             push );
  18.  
  19.     // 开始找出一个合理的排列顺序
  20.     for  ( int  ;  ; ++ )
  21.     {
  22.         // 寻找没有被任何边连向的点
  23.         if  ( empty ())  break ;        //找不到。表示目前残存的图是个环。
  24.         int   =  front ();  pop ();
  25.         ref ] = - ;                 //设为已找过(删去s点)
  26.  
  27.         cout  <<  ;                   //印出合理的排列顺序的第i点
  28.  
  29.         // 更新ref的值(删去由s点连出去的边)
  30.         for  ( int  ;  size ]; ++ )
  31.         {
  32.             int   =  adj ][ ];
  33.             ref ]--;
  34.             if  (! ref ])  push );  //纪录已经没有被任何边连向的点
  35.         }
  36.     }
  37. }

时间复杂度

时间复杂度等于一次Graph Traversal 的时间。图的资料结构为adjacency matrix 的话,便是O(V^2) ;图的资料结构为adjacency lists 的话,便是O(V+E) 。

UVa 10305 200

找出所有合理的排列顺序

请用backtracking 。此处不详述了,直接看练习题吧。

UVa 124

计算所有合理的排列顺序个数

需要使用Dynamic Programming 解决,时间复杂度O(2^V * V^2) 。

http://blog.csdn.net/tiaotiaoyly/article/details/2712349

小游戏:http://www.newgrounds.com/portal/view/527022。

Topological Sort: 
Depth-first Search

程度★ 难度★★

Depth-first Search 与Topological Sort 的关系

DFS 离开点的顺序,颠倒之后,正好是拓朴顺序。

DFS 优先走到最深的点,直到不能再深为止。DFS 也会优先找出所有最深的点,离开点的原则是最深的点先离开。最深的点当然就是拓朴顺序最后的点。

找出一个合理的排列顺序( adjacency matrix )

 
  1. bool  adj ][ ];      // adjacency matrix
  2. int  visit ];        //记录DFS遍历过的点
  3. int  order ],  ;     //储存一个合理的排列顺序
  4.  
  5. bool  cycle ;          //记录DFS的过程中是否侦测到环
  6.  
  7. void  DFS int  )
  8. {
  9.     // back edge,有环。
  10.     if  ( visit ] ==  )  cycle  =  true ;
  11.     // forward edge、cross edge。
  12.     if  ( visit ] ==  )  return ;
  13.  
  14.     visit ] =  ;
  15.     for  ( int  ;  ; ++ )
  16.         if  ( adj ][ ])
  17.             DFS );
  18.     visit ] =  ;
  19.  
  20.     order --] =  ;      //记录合理的排列顺序
  21. }
  22.  
  23. void  topological_sort ()
  24. {
  25.     // 初始化
  26.     for  ( int  ;  ;  ++)  visit ] =  ;
  27.     cycle  =  false ;
  28.      =  ;
  29.  
  30.     // 进行DFS
  31.     for  ( int  ;  ; ++ )
  32.         if  (! ])
  33.             DFS );
  34.  
  35.     // 输出结果
  36.     if  ( cycle )
  37.         cout  <<  "图上有环" ;
  38.     else
  39.         // 印出一个合理的排列顺序
  40.         for  ( int  ;  ; ++ )
  41.             cout  <<  order ];
  42. }

Activity Network

程度★ 难度★★★

Activity on Vertex Network

【待补文字】

UVa 452 10461

Activity on Edge Network

【待补文字】

UVa 506

原创粉丝点击