参考: 算法导论第三版 p356, 数据结构与算法分析p218, 算法入门经典p110
拓扑排序的两张方法:
1.dfs搜索
2.模拟人工的拓扑
两种方法的效率都是O(V + E)
$$拓扑排序的方法也是有向无环图检测的方法
/*
拓扑排序 dfs搜索 - 邻接表
可以判断一个图是否有环
*/
#include <cstdio>
#include <vector>
#include <stack>
using std::vector;
using std::stack;
const int MAXN = 10;
const int INIT = 0;
const int GRAY = 1; //参考算法导论的染色, GRAY表示一条后向边
const int BLACK = 2;
int visit[MAXN];
int time = 0;
vector<vector<int>>g(MAXN);
stack<int> ans;
bool dfs(int p) // recursion递归
{
visit[p] = GRAY; //表示正被访问(正在访问它或者它的子孙)的结点
for (int i = 0; i < g[p].size(); ++i)
{
int temp = g[p][i];
if (visit[temp] == INIT)
{
if (dfs(temp) == false)
return false;
}
else if (visit[temp] == GRAY)
{
//GRAY表示一条后向边,代表子孙指向祖先,这条边说明有环
return false; // 有环, 不是DAG(有向无环图)
}
}
visit[p] = BLACK; //已经被访问过(或者已经排序好了)
ans.push(p);
}
bool toposort(int N) // 拓扑排序 topological sort O(E + V)
{
for (int i = 1; i <= N; ++i) // 初始化
{
visit[i] = INIT;
}
time = 0;
for (int i = 1; i <= N; ++i) // 图可能不是连通的
{
if (visit[i] == INIT)
{
if (dfs(i) == false) // 递归调用dfs
return false;
}
}
return true;
}
void print()
{
while (!ans.empty())
{
printf("%d\n", ans.top());
ans.pop();
}
}
int main()
{
freopen("F://input.txt", "r", stdin);
int N, v, t;
scanf("%d", &N);
while (scanf("%d%d", &v, &t) != EOF)
{
g[v].push_back(t);//构造邻接表
}
if (toposort(N))
print();
else
printf("The graph is not a DAG\n");
}
/*
拓扑排序-模拟人工的方法-邻接表
和dfs的拓扑排序一样,这个方法也可以判断一个图是否有环
第一种没有优化, 第二种使用了队列优化
*/
#include <cstdio>
#include <vector>
#include <queue>
using std::vector;
using std::queue;
const int MAXN = 10;
vector<vector<int>>g(MAXN);
queue<int> ans; // 出队顺序保存在ans队列里面, 也可以保持在数组中
int indegree[MAXN];
bool toposort_1(int N)//没有优化,寻找度为0的点每次扫描一遍数组 O(V*V)
{
bool flag = true;
while (flag)
{
flag = false;
for (int i = 1; i <= N; ++i)
{
if (indegree[i] == 0)
{
indegree[i] = -1;// 结点i已经被排序
ans.push(i);
flag = true;
for (int j = 0; j < g[i].size(); ++j)
{
int temp = g[i][j];
--indegree[temp];
}
}
}
}
if (ans.size() < N)
return false;
return true;
}
bool toposort(int N)//队列优化, 从队列中找出度为0的结点 O(V + E)
{
queue<int> que;
for (int i = 1; i <= N; ++i)
{
if (indegree[i] == 0)
que.push(i);
}
while (!que.empty())
{
int p = que.front();
ans.push(p);
que.pop();
for (int i = 0; i < g[p].size(); ++i)
{
int temp = g[p][i];
if (--indegree[temp] == 0)//度为0的结点肯定产生在减小入度的地方
que.push(temp);
}
}
if (ans.size() < N)//如果已经排序的少于总结点数,说明图有环
return false;
return true;
}
void print()
{
while (!ans.empty())
{
printf("%d\n", ans.front());
ans.pop();
}
}
int main()
{
freopen("F://input.txt", "r", stdin);
int N, v, t;
scanf("%d", &N);
while (scanf("%d%d", &v, &t) != EOF)
{
g[v].push_back(t);//构造邻接表
++indegree[t]; // t的入度+1
}
if (toposort(N))
print();
else
printf("The graph is not a DAG\n");
}
/*
测试数据: <<数据结构与算法分析>> p218 9-4图
7
1 2
1 3
1 4
2 4
2 5
5 4
5 7
7 6
4 3
4 7
4 6
3 6
*/