【总结】拓扑排序

引入

八中OJ要升级了!!!(别开心,假的)

黎总作为八中OJ的爸爸负责人,需要监督和辅助八中OJ升级

当然,升级不是一天两天的事,黎总发现他要做的事有亿点点多,如下:

  • 让八中OJ闭关修炼(502掉)
  • 向同学们解释OJ炸掉原因
  • 加入名人堂
  • 换上更强大的后台
  • 加入各种折磨人的赛制
  • 加入Vjudge
  • OJ出关

我们假设各种赛制和Vjudge需要强大的后台,而其它内容可要可不要

那么根据常识,我们知道可以大概绘制出一个升级的图片:

在这里插入图片描述
每一个节点的执行条件为指向它的所有节点已执行

那么拓扑排序就是给出黎总一个做事的顺序让OJ正常更新而不会任何出现问题

定义

对一个 有向无环图(Directed Acyclic Graph, DAG) G G 进行拓扑排序,是将 G G 中的所有顶点排成一个线性序列,使得图中任意一对顶点 u u v v ,若边 < u , v > E ( G ) <u,v> \in E(G) ,则 u u 在线性序列中出现在 v v 之前。通常,这样的线性序列称为满足 拓扑次序(Topological Order) 的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为 拓扑排序

形象地说就是有若干件事,有些事必须在其他的一些事情干完以后才可以干,比如吃完饭以后才可以刷碗,把这些事情排成一个合理的顺序,就叫做拓扑排序。

放到图上来看,就是你要访问这个点,那你之前必须先访问一些点,拓扑排序就是在这个图上一个合法的访问顺序,按照这个顺序访问就不会出现要访问这个点的时候它所要求的要先访问的点还没访问的情况。

算法

还是让我们回到黎总视角

现在黎总毫不犹豫的选择了让OJ502!,why?,因为要让OJ502太简单了,根本不需要做什么事情铺垫。于是我们的算法第一步就来了:选择一个入度为0的点并加入拓扑序

于是黎总快乐的把机房烧了(bushi)

现在黎总发现解释原因成了目前可以做的事情,为什么?因为OJ502已经完成了呗!于是算法第二步:把刚才选择的点连出的边全部删除,并选择现在入度为 0 0 的边

于是黎总快乐的解释了原因

现在到了OJ修炼的关键时刻,怎么办,黎总面临了选择?没事,只要不让OJ走火入魔,先修炼那个招数又有什么关系呢,毕竟两招没有关联嘛。

所以任选一个即可,而这时重复刚才的操作,即 做事 \rightarrow 化简难度 \rightarrow做事\rightarrow 化简难度 \cdot \cdot \cdot \cdot\cdot \cdot

拓扑排序算法主要由以下两步循环执行,直到不存在入度为 0 0 的顶点为止。

  1. 选择一个入度为 0 0 的顶点并将它输出;
  2. 删除图中从顶点连出的所有边。

循环结束,若拓扑序中的顶点数小于图中的顶点数,则表示该图中存在回路,也就是无法进行拓扑排序;否则最终的序列就是一个拓扑序列。

代码实现

void topo(){
	queue<int> q;
	for(int i=1;i<=n;i++){
		if(indegree[i]==0){ //将所有入度为0的顶点入队 
			q.push(i); 
		}
	}
	while(!q.empty()){
		int now=q.front();//每次取出队首元素
		printf("%d ",now);
		q.pop();
		for(int i=0;i<g[now].size();i++){
			int v=g[now][i];
			indegree[v]--;
			if(indegree[v]==0){//将入度新边成0的顶点入队 
				q.push(v);
			}
		} 
	}
}

注:本文所有情节均为虚构,请勿当真

猜你喜欢

转载自blog.csdn.net/tanfuwen_/article/details/107898526