定义:给定n个顶点的有向图G,如果一个排列满足任意一个有向边(v,u),v在该排列中都在u的前面,那么这个排列称为G的拓扑排序。
注意:无环图的拓扑排序可能不止一种,有环图不存在拓扑排序。
思路:深度优先搜索(或广度优先搜索)整个图,将出度为0的顶点入栈,中途要判断是否会形成环,最后输出栈得到的序列就是该图的一种拓扑排序。
代码1(深度优先搜索):
vector<vector<int>> edges;//邻接表
vector<int> visited;//0为未搜索,1为已完成,2为搜索中
vector<int> result;//数组模拟栈
bool invalid = 0;//判断是否有环
void DFS(int v)
{
visited[v] = 2;//搜索中
for (auto u : edges[v])
{
if (visited[u] == 0)//未搜索,继续往里搜索
{
DFS(u);
if (invalid) return;
}
else if (visited[u] == 2)//说明遇到环,退出
{
invalid = 1;
return;
}
}
visited[v] = 1;
result.push_back(v);//已完成,入栈
}
//拓扑排序
void topol_sort()
{
//遍历所有顶点
for (int i = 0; i < edges.size(); ++i)
{
if (!visited[i])
{
DFS(i);
if (invalid == 1) return;
}
}
}
代码2(广度优先搜索):
vector<vector<int>> edges;//邻接表
vector<int> indeg;//储存每个顶点的入度
vector<int> result;//保存结果
bool topol_sort()
{
queue<int> q;
//将入度为0的顶点入队
for (int i = 0; i < edges.size(); ++i)
{
if (indeg[i] == 0) q.push(i);
}
while(!q.empty())
{
int v = q.front();
result.push_back(v);//加入排列
q.pop();
//删除v,并将v指向的顶点入度减一
for (auto u : edges[v])
{
if (--indeg[u] == 0) q.push(u);//如果入度为0则入队
}
}
if (result.size() != edges.size()) return 0;//说明有环,返回假
return 1;
}
两种方法的时间复杂度均为O(n+e),其中n为顶点数,e为弧数。