拓扑排序【Kahn算法(bfs)和dfs求拓扑序列及判环】

拓扑排序

对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,该排序满足这样的条件——对于图中的任意两个结点 uv,若存在一条有向边从 u指向 v,则在拓扑排序中 u一定出现在 v前面。
 
当且仅当一个有向图为有向无环图(DAG)时才存在对应于该图的拓扑排序。每一个有向无环图都 至少存在一种拓扑排序。
 
拓扑排序的实现:
①Kahn算法【常可用来判断该图是否是DAG(有向无环图)】
算法复杂度为O(v+e)。
算法实现:循环执行以下两步直到不存在入度为0的顶点为止。
(1)选择一个入度为0的顶点并输出之;(2)从网中删除此顶点及其所有出边。
循环结束后,若输出的顶点数小于网中的顶点数,则该图存在回路,否则输出的顶点序列就是拓扑序列,该图也即为DAG图。
 
该算法可借助队列实现。类似于bfs:首先将入度为0的顶点入队,取出队头元素进行拓展,找其邻接点,每个邻接点的入度-1,当入度变为0时则入队。循环此操作直到队列为空。出队顶点序列即为拓扑序列。
int k=0;
void toposort()
{
    queue<int>q;
    for(int i=1;i<=n;i++)
        if(!indegree[i])
            q.push(i);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        cout<<u<<' ';k++;//输出拓扑序列,计数+1
    for (u的每个邻接点v){ //vector或链式前向星建图,遍历方式不一样
        indegree[v]--;//删除边(u, v),即让v的入度-1;
        if (!indegree(v) )
            q.push(v);
      }
    }
} 
if (k!=n)
    存在环;
else
    不存在环,为DAG图;   

②基于DFS的拓扑排序算法( 复杂度O(V+E) )

1)DFS求给定DAG图的拓扑序列【前提:已知图是DAG】

topo数组存求得的拓扑序列
int k=0;
void DFS(int x)
{
    vis[x]=1;
    for(遍历x的邻接点j){
        if(!vis[j])
            DFS(j);
    }
    topo[k++]=x;//用栈存
}
void toposort()
{
    for(int i=1;i<=n;i++){ //遍历每个顶点
        if(!vis[i])
            DFS(i);
    }
    for(int j=k-1;j>=0;j--)
        cout<<topo[i]<<' ';//逆序输出数组,所得序列即为拓扑序列
}

2)DFS判断有向图是否有环(是否存在合法的拓扑序列):

对一个节点x进行dfs,判断是否能从x回到自己这个节点,即是否存在x到x的回路。

这里需用一个color数组标记节点的状态:-1代表未访问,0代表正在被访问,1代表已被访问过

#include<bits/stdc++.h>
using namespace std;
int n,e;//n个顶点(1<=n<=100),e条边
int color[105];//color数组表示每个结点的状态
vector<int>G[105];//vector邻接表建图
bool flag;//为真则存在环
void DFS(int x)
{
    if(flag)//如果有环就返回,否者继续搜索
        return;
    color[x]=0;//x正在被访问,状态为0
    for(int i=0;i<G[x].size();i++){
        if(color[G[x][i]]==-1)//与x相连的结点状态为-1,则该节点未被访问,继续搜索
            dfs(G[x][i]);
        else if(color[G[x][i]]==0){//与x相连的结点状态也为0,代表有环,返回
            flag=true;
            return;
        }
    }
    color[x]=1;//标记x状态为已被访问过了
}
int main()
{
    int u,v;
    while(~scanf("%d%d",&n,&e))
    {
        flag=false;
        for(int i=1;i<=n;i++)//二维vector初始化
            for(int j=0;j<G[i].size();j++)
                G[i].pop_back();
        memset(color,-1,sizeof(color));//初始化为未访问
        
        for(int i=1;i<=e;i++){
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
        }
        DFS(1);
        if(!flag)
            printf("有向图无环,存在合法的拓扑序列\n");
        else 
            printf("有向图有环,不存在合法的拓扑序列\n");
    }
    return 0;
}

 

UVA10305 给任务排序 Ordering Tasks

题意:

John有n个任务要做,每个任务在做之前要先做特定的一些任务。

输入第一行包含两个整数n和m,其中1<=n<=100。 n表示任务数,而m表示有m条任务之间的关系。 接下来有m行,每行包含两个整数i和j,表示任务i要在j之前做。

当读入两个0(i=0,j=0)时,输入结束。

输出包含q行,每行输出一条可行的安排方案。

输入:
5 4
1 2
2 3
1 3
1 5
0 0
输出:
1 4 2 5 3

题解:

裸拓扑排序

Code:

Kahn算法+vector邻接表

#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
int n,m,indegree[105];
void toposort(vector<vector<int> >G)
{
    int space=0;
    queue<int>q;
    for(int i=1;i<=n;i++)
        if(!indegree[i])
            q.push(i);
    while(!q.empty()){
        int x=q.front();q.pop();
        space?printf(" "):printf(""),space=1;
        printf("%d",x);
        for(int j=0;j<G[x].size();j++){
            indegree[G[x][j]]--;
            if(!indegree[G[x][j]])
                q.push(G[x][j]);
        }
    }
}
int main()
{
    int u,v;
    while(~scanf("%d%d",&n,&m)&&n)
    {
        vector<vector<int> >G(n+5);//开n+5行的vector二维数组
        indegree[105]={};
        while(m--){
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
            indegree[v]++;
        }
        toposort(G);
        puts("");
    }
    return 0;
}

 基于DFS的拓扑排序+链式前向星建图

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
struct node{
    int to,next;
}G[105];
int head[105],cnt;
void add(int u,int v)
{
    G[cnt].to=v;
    G[cnt].next=head[u];
    head[u]=cnt++;
}
int n,e,indegree[105],ans[105],k,vis[105];
void DFS(int rec)
{
    vis[rec]=1;
    for(int j=head[rec];~j;j=G[j].next){
        if(!vis[G[j].to])
            DFS(G[j].to);
    }
    ans[k++]=rec;
}
void toposort()
{
    for(int i=1;i<=n;i++){
        if(!vis[i])DFS(i);
    }
    int q=1;
    for(;k>0;){
        q?cout<<"":cout<<" ";
        q=0;
        cout<<ans[--k];
    }
}
int main()
{
    int u,v;
    while(~scanf("%d%d",&n,&e)&&n)
    {
        cnt=0;
        memset(vis,0,sizeof(vis));
        memset(head,-1,sizeof(head));
        for(int i=1;i<=e;i++){
            scanf("%d%d",&u,&v);
            add(u,v);
        }
        toposort();
        puts("");
    }
    return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值