《算法笔记》读书记录DAY_50

CHAPTER_10  提高篇(4)——图算法专题

10.6.1有向无环图

如果一个有向图的任意顶点都无法通过一些有向边回到自身,那么称这个有向图为有向无环图DAG)。如下给出几个DAG的例子:

10.6.2拓扑排序

拓扑排序是将有向无环图G的所有顶点排出一个线性序列,使得对图G中的任意两个顶点u和v,如果存在边u->v,那么在序列中u一定能在v的前面。这个序列又被称为拓扑序列

如下图,给出有向图(a),拓扑排序为acbfde。可以看出,这个序列完全符合拓扑排序的定义。

参照上图,拓扑排序的算法过程如下:

(1)定义一个队列Q,并把所有入度为0的结点加入队列。

(2)取队首结点输出,然后删去所有从它出发的边,并令这些边到达的顶点入度减1,如果某个结点的入度为0,则将其加入队列。

(3)反复进行(2)操作,直到队列为空。如果队列为空时入过队的结点个数恰好为N,说明拓扑排序成功,图G为有向无环图;否则,拓扑排序失败,图G中有环。

可以使用邻接表实现拓扑排序。由于需要记录结点的入度,因此需要额外建立一个数组inDegree[MAXV],并在程序一开始读入图时就记录好每个结点的入度。接下来就只需要按上面所说的步骤进行实现即可。代码如下:

vector<int> G[MAXV];
int n,m,inDegree[MAXV];

bool topologicalSort() {
	int num=0;                //记录加入拓扑序列的顶点数
	queue<int> q;
	for(int i=0;i<n;i++) {
		if(inDegree[i]==0) {
			q.push(i);        //将所有入度为0的结点入队 
		}
	}
	while(!q.empty()) {
		int u=q.front();      //取队首顶点
		//cout<<u;            必要时可以输出这个顶点
		q.pop();              //出队
		for(int i=0;i<G[u].size();i++) {
			int v=G[u][i];    //v为u的后继结点
			inDegree[v]--;    //顶点v的入度减1 
			if(inDegree[v]==0) {
				q.push(v);    //顶点v的入度减为0时则入队 
			} 
		}
		G[u].clear();         //清空u的出边,如无必要可以不写
		num++;                //拓扑序列里的顶点数+1 
	}
	if(num==n)
		return true;          //加入拓扑序列的顶点数为n则成功 
	else
		return false;         //反之拓扑排序失败 
}

拓扑排序的一个重要应用就是判断一个图是否是有向无环图,正如上面的代码,如果topologicalSort()函数返回true,则排序成功,给定的图是有向无环图;反之则给定的图中有环。

最后指出,如果要求多个入度为0的顶点中,选择最小编号的顶点优先输出,则把queue改成priority_queue即可。