图论-拓扑排序

引入

AOV网
  在日常生活中,一项大的工程可以看作是由若干个子工程(这些子工程称为“活动” )组成的集合,这些子工程(活动)之间必定存在一些先后关系,即某些子工程(活动)必须在其它一些子工程(活动)完成之后才能开始,我们可以用有向图来形象地表示这些子工程(活动)之间的先后关系,子工程(活动)为顶点,子工程(活动)之间的先后关系为有向边,这种有向图称为“顶点活动网络” ,又称“AOV网” 。
在这里插入图片描述
  在AOV网中,有向边代表子工程(活动)的先后关系,我们把一条有向边起点的活动成为终点活动的前驱活动,同理终点的活动称为起点活动的后继活动。而只有当一个活动全部的前驱全部都完成之后,这个活动才能进行。例如在上图中,只有当工程1完成之后,工程2、3、4、5、6才能开始进行。只有当2、3、4全部完成之后,7才能开始进行。
  一个AOV网必定是一个有向无环图,即不应该带有回路。否则,会出现先后关系的自相矛盾。
  在这里插入图片描述
  上图就是一个出现环产生自相矛盾的情况。4是1的前驱,想完成1,必须先完成4。3是4的前驱,而2是3的前驱,1又是2的前驱。最后造成想完成1,必须先完成1本身,这显然出现了矛盾。

拓扑排序算法


  拓扑排序算法,只适用于AOV网(有向无环图)。
  把AOV网中的所有活动排成一个序列, 使得每个活动的所有前驱活动都排在该活动的前面,这个过程称为“拓扑排序”,所得到的活动序列称为“拓扑序列”。
  一个AOV网的拓扑序列是不唯一的,例如下面的这张图,它的拓扑序列可以是:ABCDE,也可以是ACBDE,或是ADBCE。在下图所示的AOV网中,工程B和工程C显然可以同时进行,先后无所谓;但工程E却要等工程B、C、D都完成以后才能进行。
  在这里插入图片描述
构造拓扑序列可以帮助我们合理安排一个工程的进度,由AOV网构造拓扑序列具有很高的实际应用价值。

构造拓扑序列的拓扑排序算法思想:

  1. 选择一个入度为0的顶点并输出
  2. 然后从AOV网中删除此顶点及以此顶点为起点的所有关联边;
  3. 重复上述两步,直到不存在入度为0的顶点为止。
  4. 若输出的顶点数小于AOV网中的顶点数,则输出“有回路信息”,否则输出的顶点序列就是一种拓扑序列

从第四步可以看出,拓扑排序可以用来判断一个有向图是否有环。只有有向无环图才存在拓扑序列。

算法实现:
a) 数据结构:indgr[i]: 顶点i的入度;
       stack[ ]: 栈
b) 初始化:top=0 (栈顶指针置零)
c) 将初始状态所有入度为0的顶点压栈
d) I=0 (计数器)
e) while 栈非空(top>0)
i. 栈顶的顶点v出栈;top-1; 输出v;i++;
ii. for v的每一个后继顶点u
1. indgr[u]–; u的入度减1
2. if (u的入度变为0) 顶点u入栈
f) 算法结束
  这个程序采用栈来找出入度为0的点,栈里的顶点,都是入度为0的点。
我们结合上图详细讲解:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
例题:对下面有向图进行拓扑排序,

在这里插入图片描述
【参考代码】

#include<iostream>
#include<queue> 
#include<stack> 
using namespace std;
const int MAX = 35;  
int n,m;//n:顶点个数;m:边的数量 
int vex[MAX]; //vex[i]:记录以顶点i的为起点的边的编号 
int line[MAX];//记录拓扑排序结果
int indegree[MAX];//indegree[i]:记录顶点i的入度 
struct nodeEdge{ // 边类型 
	int to;//指向顶点u
	int next;//指向上一条边的位置 
}; 
nodeEdge arrPath[MAX*MAX];
//构建边
void creat_graph(int v1,int v2,int curPos){
	arrPath[curPos].to = v2;
	arrPath[curPos].next = vex[v1];
	vex[v1] = curPos;
} 
//输出顶点个数为n的拓扑序列 
void topSort(int n){
	int cnt = 0;
	stack<int> stk;
	for(int i=1;i<=n;i++){
		if( indegree[i] == 0){
			stk.push(i);
		}
	}
	while( !stk.empty() ){
		int v = stk.top();
		stk.pop();
		//访问与顶点v相连的点u,将顶点u的入度减一
		int index = vex[v];
		while(index!=-1){
			int to = arrPath[index].to;
			if( --indegree[to] == 0){
				stk.push(to);
			}
			index = arrPath[index].next;
		} 
		line[cnt] = v;
		cnt++;
	}
	if( cnt != n){
		cout<<"has circle."<<endl;
	}
	else {
		for(int i=0;i<cnt;i++){
			cout<<line[i]<<' ';
		}
	}
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		vex[i] = -1;
	}
	for(int i=1;i<=m;i++){
		int v1,v2;
		cin>>v1>>v2;
		creat_graph(v1,v2,i);
		indegree[v2]++;
	}
	topSort(n);
	return 0;
}

练习题:
洛谷-U137289 奖金
洛谷-P4017 最大食物链计数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值