拓扑排序(DFS和BFS及判断是否有环)

拓扑排序其实还是挺奇妙的,就是解决谁先谁后的问题,solve contradictions and make the world peaceful接下来就探讨一下拓扑排序的两种实现方法。

一(DFS):

1.(无环)

我们给定一个有向无环图:
拓扑排序(DFS和BFS及判断是否有环)_第1张图片
DFS是从一个点不断往下递归,比如说从序号1往下递归,有箭头就一直往下进行,直到到了最后一个元素,就开始往栈里(当然也可以是vector之类的,只不过需要反向再输出)push元素。比如说上面的从序号1开始,到序号2,序号3,序号4,到尽头了,就把4push进栈中,3push进栈,这个时候由于5也是2的下一个元素,所以5push进栈中,2push进栈,1push进栈,然后输出就是1 2 5 3 4.
当然这个递归的顺序是与你输入的顺序有关的,不过思路都是这样的,由起始点向下递归。
所以说dfs的代码还是比较简单的:

#include  
const int inf=0x3f3f3f3f;
using namespace std;
bool vis[1001];
vector<int>G[1002];   //邻接表
vector<int>ans;
void dfs(int x)
{
    int i,v;
    vis[x]=1;
    for(i=0;i<G[x].size();i++)
    {
        v=G[x][i];
        if(!vis[v])
        {
            vis[v]=1;
            dfs(v);
        }
    }
    ans.push_back(x);             //这一层完毕才把它自己扔进去。
}
int main()
{
    int n,m,i,A,B;
    cin>>n>>m;
    memset(vis,0,sizeof(vis));
    for(i=1;i<=m;i++)
    {
    cin>>A>>B;
    G[A].push_back(B);
    }
    for(i=1;i<=n;i++)
        if(!vis[i]) dfs(i);
    reverse(ans.begin(),ans.end());
    for(i=0;i<ans.size();i++)
        printf("%d%c",ans[i],i==ans.size()-1?'\n':' ');
}

2.(有环)

dfs判断是否有环只需要把vis[]的状态改一下,原本是两种状态,0和1,现在改成0,1,-1, 0代表未访问,-1代表访问完毕,1代表是这一阶段正在访问的(这一阶段指的是两个元素在同一个递归中)。我只是列出改动的一部分。

int dfs(int x)
{
    int i,v;
    vis[x]=1;
    for(i=0;i<G[x].size();i++)
    {
        v=G[x][i];
        if(vis[v]==1)
            {
                return 0;
            }
        else if(vis[v]==0 && !dfs(v))
        {
            return 0;
        }
    }
    vis[x] = -1;
    ans.push_back(x);
    return true;
}

二(BFS):

1.(无环)

依旧是给出上面的图:
拓扑排序(DFS和BFS及判断是否有环)_第2张图片
我们依旧是有一个明确的思路:每一个顶点都有入度和出度,入度为0说明没有指向他的,那么就从他开始往下找。把这个入度为0的push进队列(还要注意保存入度为0的点),同时把与这个点相连的所有点的入度-1,然后再看看有没有入度为0的,有的话继续push,循环上面的操作,直到没有入度为0的点。
看一下上面的图,如果从序号1开始的话:1入度为0,push进队列,顶点2的入度-1,所以顶点2push进队列,3和5的入度-1,3push进队列,5push进队列,顶点3进队列后,顶点4入度-1,顶点4push进队列,所以输出结果就是1 2 3 5 4

#include 
using namespace std;
vector<int>G[1002];
vector<int>ans;
int vis[1002];
int in[1002],n;
void bfs(int x)
{
    int i,u,v;
    queue<int>Q;
    vis[x]=1;
    Q.push(x);
    while(!Q.empty())
    {
        u=Q.front();
        Q.pop();
        ans.push_back(u);
        for(i=0;i<G[u].size();i++)
        {
            v=G[u][i];
            in[v]--;
            if(!vis[v] && !in[v])
            {
                vis[v]=1;
                Q.push(v);
            }
        }
    }
}
int main()
{
    int m,i,A,B;
    scanf("%d %d",&n,&m);
    memset(in,0,sizeof(in));
    memset(vis,0,sizeof(vis));
    for(i=1;i<=m;i++)
    {
        cin>>A>>B;
        G[A].push_back(B);                 //邻接表建图
        in[B]++;                   //统计入度
    }
    for(i=1;i<=n;i++)
    if(!in[i] && !vis[i])
    bfs(i);
    for(i=0;i<ans.size();i++)
        printf("%d%c",ans[i],i==ans.size()-1?'\n':' ');
}

或者是可以更加简便一些:我们可以一开始就把所有入度为0的点push进队列,而且这样的话,vis[]数组也可以省略。可以自己试一下。

for(i=1;i<=n;i++)
      if(!in[i])
        Q.push(i);

2.(有环)

BFS的判断是否有环就很简单了,只要入队(或者出队)的次数不等于节点的个数,就说明有环。因为我们每次出队都需要记录下这些节点,共有N个节点,必须有N次出队才行,如果有环,出队的次数就会小于N,因为它们的入度有猫腻呗,所以就不贴代码了,只是一点小小的改动。自己试一下吧。

你可能感兴趣的:(拓扑排序)