拓扑排序

定义:将有向图中的顶点以线性方式进行排序。即对于任何连接自顶点u到顶点v的有向边uv,在最后的排序结果中,顶点u总是在顶点v的前面。

前提条件: 必须是有向无环图(可以不连通)

偏序/全序关系:

所谓偏序,就是图中没有环(自己出发不能回到自己)

所谓全序,就是在偏序的基础之上,有向无环图中的任意一对顶点还需要有明确的关系(有唯一拓扑序)(反映在图中,就是单向连通的关系,注意不能双向连通,那就成环了)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1285

算法—:Kahn算法(可加第二关键字)

 

该算法的实现十分直观,关键在于需要维护一个入度为0的顶点的集合:

 

每次从该集合中取出(没有特殊的取出规则)一个顶点,将该顶点放入保存结果的List中。

 

紧接着循环遍历由该顶点引出的所有边,从图中移除这条边,同时获取该边的另外一个顶点,如果该顶点的入度在减去本条边之后为0,那么也将这个顶点放到入度为0的集合中。然后继续从集合中取出一个顶点直到集合为空。

当集合为空之后,检查图中是否还存在任何边,如果存在的话,说明图中至少存在一条环路。不存在的话则返回结果List,此List中的顺序就是对图进行拓扑排序的结果。

/****************************************************************************************************

                                                拓扑排序 
                                                Kahn算法
                        

********************************************************************************************************/
#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
using namespace std;
priority_queue<int>a;
vector<int>v[505];
int size[505],list[505],top=0;//size为每个点入度,list为结果数组 
int main(){
    int n,m,x,y;
    while(scanf("%d%d",&n,&m)!=EOF){
        top=0;
        memset(size,0,sizeof(size));
        while(!a.empty())a.pop();
        for(int i=0;i<m;i++){
            scanf("%d%d",&x,&y);
            v[x].push_back(y);
            size[y]++;
        }
        for(int i=1;i<=n;i++){//将所有入度为0的顶点加入队列
            if(!size[i])a.push(-i);
        }
        while(!a.empty()){
            int u=-a.top();
            a.pop();
            list[top++]=u;
            for(int i=0;i<v[u].size();i++){//u的每个邻接点v
                size[v[u][i]]--;//删除边(u, v);
                if(!size[v[u][i]])a.push(-v[u][i]);//如果删除边后v入度为0,则将v加入队列
            }
            v[u].clear();
        }
        bool d=0;
        for(int i=1;i<=n;i++){//如果图中还有边存在则存在环
            if(!v[i].empty())v[i].clear(),d=1;
        }
        if(d)printf("wrong\n");
        else{
            for(int i=0;i<top-1;i++){
                printf("%d ",list[i]);
            }
            printf("%d",list[top-1]);
            printf("\n");
        }
    }
    return 0;
}

 二.基于bfs的拓扑排序(不可加第二关键字)

对于每个节点进行一遍dfs,dfs到的节点都在当前节点前面,没有遍历到的都与当前节点无关

http://acm.hdu.edu.cn/showproblem.php?pid=3342

/****************************************************************************************************

                                                拓扑排序 
                                            基于DFS的拓扑排序
                        

********************************************************************************************************/
#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
using namespace std;
vector<int>g[505];
int vis[505],list[505],top=0;//vis表示节点访问状态,list为结果数组 
bool dfs(int u)  
{  
    vis[u] = -1;//-1用来表示顶点u正在访问  
    for(int i = 0 ; i < g[u].size() ; i ++)  {  
        if(vis[g[u][i]] == -1)//表示这个点进入了两次,肯定出现了环  
            return false;  
        else if(vis[g[u][i]] == 0)  {  
            if(!dfs(g[u][i]))return false;  
        }  
    }  
    vis[u] = 1;
    list[top++] = u;//放到结果数组里,输出的时候记得倒序输出,(回溯的原因)  
    return true;  
}  
int main(){
    int n,m,x,y;
    while(scanf("%d%d",&n,&m)!=EOF&&(n||m)){
        bool d=0;
        memset(vis,0,sizeof(vis));
        for(int i=0;i<n;i++)g[i].clear();
        top=0;
        for(int i=0;i<m;i++){
            scanf("%d%d",&x,&y);
            g[x].push_back(y);
        }
        for(int i=0;i<n;i++){
            if(!vis[i])if(!dfs(i))d=1;
        }
        if(d)printf("NO\n");
        else printf("YES\n");
    } 
    return 0;
} 

 

posted @ 2017-04-17 21:25  Bennettz  阅读(220)  评论(0编辑  收藏  举报