7.5有向无环图应用之拓扑排序

7.5有向无环图应用之拓扑排序

有向无环图(Directed Acyclic Graph)是指一个无环的有向图,简称 DAG。有 向无环图可用来描述工程或系统的进行过程,如一个工程的施工图、学生课程间的制约关系图等。

拓扑排序
用顶点表示活动,用弧表示活动间的优先关系的有向无环图,称为顶点表示 活动的网(Activity On Vertex Network), 简称为 AOV-网。

例如:计算机系学生的一些必修课程及其先修课程的关系如下:
在这里插入图片描述
用顶点表示课程,弧表示先决条件,则上述关系可用一个有向无环图表示, 见下图
在这里插入图片描述
在有向图 G=(V,{E})中, V 中顶点的线性序列(vi1,vi1,vi3,…,vin)称为 拓扑序列。如果此序列满足条件:对序列中任意两个顶点 vi、vj,在 G 中有一条从vi到vj的路径,则在序列中vi必排在vj之前。

例如,上图的一个拓扑序列为:
AOV-网的特性如下:

  • 若 vi为 vj的先行活动,vj为 vk的先行活动,则 vi必为 vk的先行活动,即先 行关系具有可传递性。从离散数学的观点来看,若有<vi,vj>、<vj,vk>,则必 存在<vj,vk>。显然,在 AOV-网中不能存在回路,否则回路中的活动就会互为前驱,从而无法执行。
  • AOV-网的拓扑序列不是惟一的。
    例如,上图的另一个拓扑序列为:
    那么,怎样求一个有向无环图的拓扑序列呢?拓扑排序(Topological Sort) 的基本思想为:
    (1) 从有向图中选一个无前驱的结点输出;
    (2) 将此结点和以它为起点的边删除;
    (3) 重复(1)、( 2),直到不存在无前驱的结点;
    (4) 若此时输出的结点数小于有向图中的顶点数,则说明有向图中存在 回路,否则输出的顶点的顺序即为一个拓扑序列。

例如,对于下图中的 AOV-网,执行上述过程可以得到如下拓扑序列: V1,V6,V4,V3,V2,V5 或 V1,V3,V2,V6,V4,V5。
在这里插入图片描述
由于有向图的存储形式的不同,拓扑排序算法的实现也不同。

(1)基于邻接矩阵表示的存储结构

A 为有向图 G 的邻接矩阵,则有

  • 找 G 中无前驱的结点 ——在 A 中找到值全为 0 的列;
  • 删除以 i 为起点的所有弧 ——将矩阵中 i 对应的行全部置为 0。
    算法步骤如下:
    ①取 1 作为第一新序号;
    ②找一个未新编号的、值全为 0 的列 j,若找到则转③;否则,若所有的 列全部都编过号,排序结束; 若有列未曾被编号,则该图中有回路;
    ③输出列号对应的顶点 j,把新序号赋给所找到的列;
    ④将矩阵中 j 对应的行全部置为 0;
    ⑤新序号加 1,转②;

(2)基于邻接表的存储结构

此时入度为 0 的顶点即没有前驱的顶点,因此可以附设一个存放各顶点入度 的数组 indegree [ ],于是有

  • ①找 G 中无前驱的顶点 ——查找 indegree [ i]为零的顶点 i;
  • ②删除以 i 为起点的所有弧——对链在顶点 i 后面的所有邻接顶点 k,将对 应的 indegree[k]减 1。
    为了避免重复检测入度为零的顶点,可以再设置一个辅助栈,若某一顶点的入度减为 0,则将它入栈。每当输出某一顶点时,便将它从栈中删除。
    算法思想
  • (1) 首先求出各顶点的入度,并将入度为 0 的顶点入栈;
  • (2) 只要栈不空,则重复下面处理:
    ①将栈顶顶点 i 出栈并打印;
    ②将顶点 i 的每一个邻接点 k 的入度减 1,如果顶点 k 的入度变为 0,则将 顶点 k 入栈。

算法描述】 拓扑排序算法

int TopoSort (AdjList G) 
{  
	Stack S; 
	int indegree[MAX_VERTEX_NUM]; 
	int i, count, k;     
	ArcNode *p;     
	FindID(G,indegree);  /*求各顶点入度*/     
	InitStack(&S);       /*初始化辅助栈*/     
	for(i=0;i<G.vexnum;i++)     
		if(indegree[i]==0) Push(&S,i);    /*将入度为 0 的顶点入栈*/     
			count=0;     
		while(!IsEmpty(S))       
		{ 
			Pop(&S,&i);      
			printf("%c", G.vertex[i].data);      
			count++;    /*输出 i 号顶点并计数*/         
			p=G.vertex[i].firstarc;  
			while(p!=NULL)    
			{ 
				k=p->adjvex;           
				indegree[k]--;     /*i 号顶点的每个邻接点的入度减 1*/      
				if(indegree[k]==0)  
				Push(&S, k);   /*若入度减为 0,则入栈*/      
				p=p->nextarc;     
			}    
		} /*while*/     
	if (count<G.vexnum)  
			return(Error);  /*该有向图含有回路*/      
	else  
		return(Ok);  
} 

求入度算法

void FindID( AdjList G,  int indegree[MAX_VERTEX_NUM])  /*求各顶点的入度*/ 
{  
	int i;  
	ArcNode *p;  
	for(i=0; i<G.vexnum; i++)        
		indegree[i]=0;       
	for(i=0; i<G.vexnum; i++)       
	{
		p=G.vertex[i].firstarc;     
		while(p!=NULL)          
		{
			indegree[p->adjvex]++;          
			p=p->nextarc;         
		}       
	} /* for */    
}   

例如,上图 AOV-网的邻接表如下 所示,用拓扑排序算法求出的拓扑序列为: v6,v1,v3,v2,v4,v5。
在这里插入图片描述
若有向无环图有 n 个顶点和 e 条弧,则在拓扑排序的算法中,for 循环需要 执行 n 次,时间复杂度为 O(n);对于 while 循环,由于每一顶点必定进一次栈, 出一次栈,其时间复杂度为 O(e);故该算法的时间复杂度为 O(n+e)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值