QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 6569|回复: 3
打印 上一主题 下一主题

拓扑排序

[复制链接]
字体大小: 正常 放大
韩冰        

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

跳转到指定楼层
1#
发表于 2004-10-4 05:23 |只看该作者 |倒序浏览
|招呼Ta 关注Ta

一个复杂的工程通常可以分解成一组小任务的集合,完成这些小任务意味着整个工程的完成。例如,汽车装配工程可分解为以下任务:将底盘放上装配线,装轴,将座位装在底盘上,上漆,装刹车,装门等等。任务之间具有先后关系,例如在装轴之前必须先将底板放上装配线。任务的先后顺序可用有向图表示——称为顶点活动( Activity On Vertex, AOV)网络。有向图的顶点代表任务,有向边(i, j) 表示先后关系:任务j 开始前任务i 必须完成。图1 - 4显示了六个任务的工程,边( 1 , 4)表示任务1在任务4开始前完成,同样边( 4 , 6)表示任务4在任务6开始前完成,边(1 , 4)与(4 , 6)合起来可知任务1在任务6开始前完成,即前后关系是传递的。由此可知,边(1 , 4)是多余的,因为边(1 , 3)和(3 , 4)已暗示了这种关系。

7 x, V0 G# J1 `3 S. P: J- b

在很多条件下,任务的执行是连续进行的,例如汽车装配问题或平时购买的标有“需要装配”的消费品(自行车、小孩的秋千装置,割草机等等)。我们可根据所建议的顺序来装配。在由任务建立的有向图中,边( i, j)表示在装配序列中任务i 在任务j 的前面,具有这种性质的序列称为拓扑序列(topological orders或topological sequences)。根据任务的有向图建立拓扑序列的过程称为拓扑排序(topological sorting)。图1 - 4的任务有向图有多种拓扑序列,其中的三种为1 2 3 4 5 6,1 3 2 4 5 6和2 1 5 3 4 6,序列1 4 2 3 5 6就不是拓扑序列,因为在这个序列中任务4在3的前面,而任务有向图中的边为( 3 , 4),这种序列与边( 3 , 4)及其他边所指示的序列相矛盾。可用贪婪算法来建立拓扑序列。算法按从左到右的步骤构造拓扑序列,每一步在排好的序列中加入一个顶点。利用如下贪婪准则来选择顶点:从剩下的顶点中,选择顶点w,使得w 不存在这样的入边( v,w),其中顶点v 不在已排好的序列结构中出现。注意到如果加入的顶点w违背了这个准则(即有向图中存在边( v,w)且v 不在已构造的序列中),则无法完成拓扑排序,因为顶点v 必须跟随在顶点w 之后。贪婪算法的伪代码如图1 3 - 5所示。while 循环的每次迭代代表贪婪算法的一个步骤。

+ E3 V. [5 |. W5 @

现在用贪婪算法来求解图1 - 4的有向图。首先从一个空序列V开始,第一步选择V的第一个顶点。此时,在有向图中有两个候选顶点1和2,若选择顶点2,则序列V = 2,第一步完成。第二步选择V的第二个顶点,根据贪婪准则可知候选顶点为1和5,若选择5,则V = 2 5。下一步,顶点1是唯一的候选,因此V = 2 5 1。第四步,顶点3是唯一的候选,因此把顶点3加入V

- ]) h3 l1 C% j# R

得到V = 2 5 1 3。在最后两步分别加入顶点4和6 ,得V = 2 5 1 3 4 6。

& l6 }* v7 g3 }/ M9 z/ O; b/ _8 W

1. 贪婪算法的正确性

0 @8 |4 N& D6 q) O9 A+ W# Q5 B

为保证贪婪算法算的正确性,需要证明: 1) 当算法失败时,有向图没有拓扑序列; 2) 若

3 H2 u( g1 L1 a/ r3 x

算法没有失败,V即是拓扑序列。2) 即是用贪婪准则来选取下一个顶点的直接结果, 1) 的证明见定理1 3 - 2,它证明了若算法失败,则有向图中有环路。若有向图中包含环qj qj + 1.qk qj , 则它没有拓扑序列,因为该序列暗示了qj 一定要在qj 开始前完成。

4 V5 n# O" f7 \

定理1-2 如果图1 3 - 5算法失败,则有向图含有环路。

, q, C9 P' n) B/ j' p- @

证明注意到当失败时| V | 7 @0 v9 J$ }$ |" T+ n/ P

2. 数据结构的选择

3 U1 S4 @" B8 m3 B# R; |: n

为将图1 - 5用C + +代码来实现,必须考虑序列V的描述方法,以及如何找出可加入V的候选顶点。一种高效的实现方法是将序列V用一维数组v 来描述的,用一个栈来保存可加入V的候选顶点。另有一个一维数组I n D e g r e e,I n D e g r e e[ j ]表示与顶点j相连的节点i 的数目,其中顶点i不是V中的成员,它们之间的有向图的边表示为( i, j)。当I n D e g r e e[ j ]变为0时表示j 成为一个候选节点。序列V初始时为空。I n D e g r e e[ j ]为顶点j 的入度。每次向V中加入一个顶点时,所有与新加入顶点邻接的顶点j,其I n D e g r e e[ j ]减1。对于有向图1 - 4,开始时I n D e g r e e [ 1 : 6 ] = [ 0 , 0 , 1 , 3 , 1 , 3 ]。由于顶点1和2的I n D e g r e e值为0,因此它们是可加入V的候选顶点,由此,顶点1和2首先入栈。每一步,从栈中取出一个顶点将其加入V,同时减去与其邻接的顶点的I n D e g r e e值。若在第一步时从栈中取出顶点2并将其加入V,便得到了v [ 0 ] = 2,和I n D e g r e e [ 1 : 6 ] = [ 0 , 0 , 1 , 2 , 0 , 3 ]。由于I n D e g r e e [ 5 ]刚刚变为0,因此将顶点5入栈。

6 ?. B1 e; f; E+ S

程序1 3 - 2给出了相应的C + +代码,这个代码被定义为N e t w o r k的一个成员函数。而且,它对于有无加权的有向图均适用。但若用于无向图(不论其有无加权)将会得到错误的结果,因为拓扑排序是针对有向图来定义的。为解决这个问题,利用同样的模板来定义成员函数AdjacencyGraph, AdjacencyWGraph,L i n k e d G r a p h和L i n k e d W G r a p h。这些函数可重载N e t w o r k中的函数并可输出错误信息。如果找到拓扑序列,则Topological 函数返回t r u e;若输入的有向图无拓扑序列则返回f a l s e。当找到拓扑序列时,将其返回到v [ 0 :n- 1 ]中。

9 E. b3 K6 ?3 N- r& M

3. Network:Topological 的复杂性

2 g9 D$ }7 g; T/ m. _

第一和第三个f o r循环的时间开销为(n )。若使用(耗费)邻接矩阵,则第二个for 循环所用的时间为(n2 );若使用邻接链表,则所用时间为(n+e)。在两个嵌套的while 循环中,外层循环需执行n次,每次将顶点w 加入到v 中,并初始化内层while 循环。使用邻接矩阵时,内层w h i l e循环对于每个顶点w 需花费(n)的时间;若利用邻接链表,则这个循环需花费dwout 的时间,因此,内层while 循环的时间开销为(n2 )或(n+e)。所以,若利用邻接矩阵,程序1 3 - 2的时间复杂性为(n2 ),若利用邻接链表则为(n+e)。

+ {1 q P; z' |- T! Q. s

程序13-2 拓扑排序

% ~: P0 A3 _! _ L& W" o" g/ L

bool Network::Topological(int v[])

' w% A9 J- ~/ g, l

{// 计算有向图中顶点的拓扑次序

2 t! j# C6 v8 b& B

// 如果找到了一个拓扑次序,则返回t r u e,此时,在v [ 0 : n - 1 ]中记录拓扑次序

6 O8 n1 {+ Z7 n, t% f

// 如果不存在拓扑次序,则返回f a l s e

2 L) B9 C# i* v0 l1 a

int n = Ve r t i c e s ( ) ;

3 P3 E1 ?' G, ]! i& S( q

// 计算入度

. p" [ v. s6 G& g' Z3 h8 H' ~

int *InDegree = new int [n+1];

+ ~" c/ k9 k& c' G) T E

InitializePos(); // 图遍历器数组

) e7 A* x. V/ b2 A! r

for (int i = 1; i <= n; i++) // 初始化

1 F% o0 y j2 O5 O

InDegree = 0;

. A; O1 [1 m, u$ {8 A6 M

for (i = 1; i <= n; i++) {// 从i 出发的边

* @4 {) S3 s2 V. E2 t4 g5 i

int u = Begin(i);

8 F. f" h) I2 p( z" w4 C

while (u) {

3 H6 [9 L" O3 k* E2 e

I n D e g r e e [ u ] + + ;

! @+ }3 V9 u- m5 P5 V# o

u = NextVe r t e x ( i ) ; }

4 \1 {& s' [. d/ w7 N

}

d# b0 j( i5 _; n i$ S; A

// 把入度为0的顶点压入堆栈

' O8 [, t% T9 P

LinkedStack S;

# o2 l. [, ~1 u- y: |; N" I! a

for (i = 1; i <= n; i++)

* @) I* p* m5 h6 L/ p- U

if (!InDegree) S.Add(i);

4 F, \5 I" z& H5 V8 w( A" z5 _1 a

// 产生拓扑次序

2 @" r+ m5 P) E$ F( Z+ h/ E

i = 0; // 数组v 的游标

! B# L# ?# o1 c0 v" u

while (!S.IsEmpty()) {// 从堆栈中选择

# l. k0 e l0 P9 I r

int w; // 下一个顶点

_2 d5 O& e) M- B: Q: i

S . D e l e t e ( w ) ;

1 U n8 k" N/ D2 X' Y; P0 L8 y

v[i++] = w;

. m/ y5 }2 r) Z8 x" F' O/ n; R$ n

int u = Begin(w);

( E7 Y1 ~9 j. t' e

while (u) {// 修改入度

% l4 G% o) N7 [' m5 ~. R. U

I n D e g r e e [ u ] - - ;

1 T( N( q" g4 B4 {+ K @& g) P4 F1 z; _

if (!InDegree) S.Add(u);

0 }5 L2 v5 {5 J: s- L

u = NextVe r t e x ( w ) ; }

( @' u; C4 f2 z4 J

}

: \. O( F8 a; e- g) L( p7 ^

D e a c t i v a t e P o s ( ) ;

3 Y# V, L/ n7 H, C

delete [] InDegree;

# u* w4 ~1 j+ y- k/ O1 u1 A, k+ z

return (i == n);

6 v1 s4 ?6 o) C5 t; v4 c! d# C

}

zan
转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
xiaomeizi 实名认证       

0

主题

3

听众

97

积分

升级  96.84%

该用户从未签到

自我介绍
我是一个贪玩的孩子啊
没太看明白!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
回复

使用道具 举报

1

主题

9

听众

297

积分

升级  98.5%

  • TA的每日心情
    开心
    2024-9-1 01:07
  • 签到天数: 34 天

    [LV.5]常住居民I

    群组数学建模培训课堂2

    回复

    使用道具 举报

    0

    主题

    3

    听众

    454

    积分

    升级  51.33%

  • TA的每日心情
    慵懒
    2014-3-18 21:46
  • 签到天数: 147 天

    [LV.7]常住居民III

    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 注册地址

    qq
    收缩
    • 电话咨询

    • 04714969085
    fastpost

    关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

    手机版|Archiver| |繁體中文 手机客户端  

    蒙公网安备 15010502000194号

    Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

    GMT+8, 2025-7-20 01:45 , Processed in 0.853932 second(s), 69 queries .

    回顶部