有向无环图(DAG)的所有拓扑序列

来源:互联网 发布:知乎 学古文的外国人 编辑:程序博客网 时间:2024/04/28 15:57

拓扑序列:

当一个有向图无环时,会存在拓扑序列。即将有向图G中的顶点按照线性序列排列,使得G中的任意两个顶点uv ,使得 for (u,v) in Edges(G), 在线性序列中都满足u出现在v的前面。

求一个DAG的一条拓扑序列很好求,只要按照如下步骤即可:

1.       找到当前所有的无直接前驱的vertex(未曾访问过),若没有这样的顶点,跳到3。若有,从中选一个v,标记为已访问,加入到当前序列的尾部,继续2

2.       将从v出发的有向边全部删除(这样会得到一个新的有向图G’)

3.       如果序列中的顶点数不等于有向图G的顶点个数,则说明图G中存在环;如果相等,则该序列即是所求的拓扑序列。

 

现在要求有向图G的所有拓扑序列。

可采用回溯法。

先看个小例子:

有三个集合ABC,每个集合中都有若干个顶点,要求从三个集合中依次(先从AB最后从C)取出一个顶点,组成一个序列,问这样的序列有多少。

假设A={v1, v2, v3}   B={v4, v5}  C={v6, v7, v9}

显然有3*2*3=18种这种序列。

: v1->v4->v6   v1->v4->v7   v1->v4->v9   v1->v5->v6  v1->v5->v7  …  v3->v5->v9

自然通过回溯很好将这些序列全部输出:

Index[3] = {0};

data[0] = {v1, v2, v3}

data[1]={v4, v5}

data[2]={v6, v7, v8}

length[3]={3,2,3}

result[3];

i= 0;

while(i>=0)

{

         If(i<3)   //继续向下搜索解,3代表欲求序列的长度

         {

                   If(index[i] < length[i])   //还有可能的取值, index[i]表示当前可能取值的下标

                   {

                            result[i]=data[i][ index[i] ];

                            index[i]++;

                            i++;

                   }

                   else   //无可能的取值了,回溯

                   {

                            tmp = i;

                            i--;

                            while(tmp <3) //将后面的可能取值的索引下标都置为初始值0, 使得回溯到前一个后,后面重新开始

                                     index[tmp++]=0;

                   }

         }

         else

         {

                   //得到一个解了

                   Print(result)

                   i--;  //再回溯回去,继续求解

         }

}

 

 

 

此处我们求所有拓扑序列也可以用回溯的方法。

每当我们执行上述求单一拓扑序列的第一步时,我们得到一个顶点集合S,我们在每个集合里面都设个索引标记index,表示当前我取得是集合里面的第几个顶点,这样就可以跟上例一样,通过index来取顶点&&判断当前集合中的顶点是否都已经用过了。

 

假设有向图G的顶点个数为V,则我们的拓扑序列的元素个数也应该是V,我们用result[V]来存放当前的拓扑序列。假设我们当前正在处理第i个拓扑序列中的元素result[i]

执行步骤如下:

1.       求当前有向图中所有无直接前驱的顶点集合S(i),设属于集合的索引index=0,如果集合非空,则跳转到2,;如果集合为空,则跳转到3.

2.       如果index<length(S(i)),取S(i)[index]作为当前序列元素result[i]的值,并且将index1,将从顶点result[i]出去的有向边全部从有向图中去掉,但是将去掉的有向边全部记录下来(等回溯的时候恢复).再接着求序列的下一个元素result[i+1]的值,跳转到1继续执行。

如果index>length(S(i)),即index的值如果大于集合S(i)的大小了,说明当前集合S(i)中的值都用过了,必须回溯到前一个序列元素result[i-1],将它的值变成它对应集合(S(i-1))中的下一个可能的值.假设之前result[i-1]使用的是S(i-1)[n],那么现在result[i-1]就应该使用s(i-1)[n+1],然后继续向后搜索(继续执行1),当然在使用该值之前需要将之前删除的以s(i-1)[n]为出发点的有向边全部恢复,自然如果这里的n的大小已经等于S(i-1)中元素的个数了,那么序列继续回溯:i1,并且恢复删除的边,尝试新的取值。当i回溯到-1,求值过程也就结束了

3.       如果当前序列中的元素个数等于V,说明已经求得了一个拓扑序列,输出,然后回溯,尝试着继续求其他可能的拓扑序列。

 

这题好似数据结构的学期课程设计题之一。

有种做法很二笔,先求所有顶点的排序序列,在得到一个序列后再判断这个序列是否是有向无环图的一个拓扑序列,这个时间是指数级的。这个方法很好,不过这个方法的编码实现也不是那么好搞,具体代码下载:http://download.csdn.net/download/peibaoyi/5744269

代码中的顶点从0开始编号,边是通过顶点的编号来指定的.

原创粉丝点击