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

拓扑排序node

对一个有向无环图(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) )ios

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

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表明已被访问过spa

#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.net

题意code

John有n个任务要作,每一个任务在作以前要先作特定的一些任务。blog

输入第一行包含两个整数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; }
相关文章
相关标签/搜索