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即可。