拓扑排序(定义):
对于有向图的节点按照访问顺序排序,最终形成一个线性序列(该序列满足:每个顶点出现且只出现一次)
不难发现:如果图中存在有向环,则不存在拓扑排序
拓扑排序(演示)
- 利用深度优先搜索排序
代码参考《算法竞赛入门经典(第2版)》
int c[maxn]; //c[u]=0表示没有访问过,c[u]=1表示已经访问过,c[u]=-1表示正在访问
int topo[maxn],t; //数组模拟栈,t是顶点总数
bool dfs(int u)
{
c[u] = -1; //从u开始dfs,正在访问u,标记为-1
for (int v = 0; v < n; v++) //遍历每一个顶点
{
if (G[u][v]) //有从u到v的指向
{
if (c[v] < 0) return false;//存在有向环,返回false
if (!c[v] && !dfs(v)) return false;// 如果「未搜索」那么搜索相邻节点,但如果搜索后发现有环,返回false
}
}
//如果前面都没有返回,说明从u开始的有向图没有环
//访问标志设为1
//在访问完一个节点之后把它加到当前拓扑排序的首部
//从u开始的dfs,生成一个u在首部的topo数组(此时的topo数组不一定包括所有节点,因此toposort()函数仍然需要遍历所有节点)
c[u] = 1; topo[--t] = u;
return true;
}
bool toposort()
{
t = n;
memset(c, 0, sizeof(c));
for (int u = 0; u < n; u++)//每次挑选一个「未搜索」的节点,开始进行深度优先搜索(考虑到有些节点是独立的)
{
if (!c[u])
{
if (!dfs(u)) return false;
}
}
return true;
}
- 利用广度优先搜索排序
不难理解,将入度为0的顶点压入队列,该顶点指向的顶点的度数-1
代码参考:拓扑排序入门(真的很简单)
queue<int>q;
vector<int>edge[n];
for(int i=0;i<n;i++) //n 节点的总数
if(in[i]==0) q.push(i); //将入度为0的点入队列
vector<int>ans; //ans 为拓扑序列
while(!q.empty())
{
int p=q.front(); q.pop(); // 选一个入度为0的点,出队列
ans.push_back(p); //压入拓扑序列
for(int i=0;i<edge[p].size();i++)
{
int y=edge[p][i];
in[y]--;
if(in[y]==0)
q.push(y);
}
}
if(ans.size()==n)
{
for(int i=0;i<ans.size();i++)
printf( "%d ",ans[i] );
printf("\n");
}
else printf("No Answer!\n"); // ans 中的长度与n不相等,就说明无拓扑序列