浅谈拓扑排序(基于dfs算法)

假设有n个任务,有m个有序对(u,v),表示任务u应该排在任务v之前,那么怎样将这些任务按照顺序排列起来呢?比如有三个有序对(1,4),(3,2),(1,3)排列起来就是1,3,2,4 。尽管还有其他可能(如1,4,3,2),但我们只需找出一种即可,注意:有些情况无法排序,如(1,2),(2,3),(3,1)。

我们把每个任务看成一个点,将每个有序对看成有向边,则形成了一个有向图,由题意可知这个有向图必须是无环的,否则无法排序。在图论中,我们把对这些节点进行排序的问题称为拓扑排序,可借用dfs算法解决该问题。

代码如下:

#include<iostream>
#include<cstring>
using namespace std;
int n,m,topo[100];       //topo数组用来储存最终形成的拓扑序列
int G[100][100];          //储存有序对信息
int c[100];                   //储存每个节点是否被访问过的信息
int t;                          
bool dfs(int u)
{
    c[u]=-1;               //该段代码的一个亮点,表示u节点正在被访问
    for(int v=1;v<=n;v++)
    {
        if(G[u][v])
        {
            if(c[v]==-1)   //访问到正在访问的节点,即为存在有向环
            return false;
            if(c[v]==0)
            {
                if(!dfs(v))   //深度优先遍历
                return false;
            }
        }
    }
    c[u]=1;            //返回时将该节点标记为已访问过
    topo[–t]=u;   //将此节点插入拓扑序列
    return true;
}
int main()
{
 int a,b;
 cin>>n>>m;
 t=n;
 memset(G,0,sizeof(G));
 memset(c,0,sizeof(c));
 for(int i=0;i<m;i++)
 {
     cin>>a>>b;
     G[a][b]=1;
 }
 for(int u=1;u<=n;u++)
 {
     if(!c[u])                      //如果该节点没有被访问过
     if(!dfs(u))                   //dfs函数对图中节点进行深度优先遍历,返回值表示拓扑排序是否存在
     {cout<<“存在有向环,失败退出”<<endl;
     return 0;}
 }
 for(int i=0;i<n;i++)
 cout<<topo[i]<<” “<<endl;
 return 0;
}

即使我加了好多好多注释,可对于新手玩家而言还是有些难理解,那么我再举个例子。

比如两个有序对(1,2),(1,3),此时这三个节点形成一棵树,父节点为1,子节点为2和3,我们dfs对它进行访问。

首先进入父节点1,标记为正在访问,然后进入2,由于2不存在子节点,保存2并退回父节点,又访问3,同样保存并退回,此时1才算访问结束,在拓扑序列中插入到2和3的前面。

从2或者3开始结果也是一样的哦,大家不妨试一试。

    原文作者:拓扑排序
    原文地址: https://blog.csdn.net/langzitan123/article/details/79687736
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞