拓扑排序+有向无环图(DAG)的检测

参考: 算法导论第三版 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
*/
    原文作者:拓扑排序
    原文地址: https://blog.csdn.net/li_jiakang/article/details/40662321
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞