拓扑排序
Kahn算法
- 找到没有前置活动的点,也就是入度为0的点,存入暂存区(可以是stack或queue)
- 从暂存区中取出一个入度为0的点,该点表示的活动完成,这个活动完成后可能会产生新的没有前置活动的点,所以需要遍历这个点的出度,出度节点的入度减一,再检查出度节点的入度减一后是否为0,是0就放入暂存区
- 直到暂存区为空,结束循环
由于节点的入度可能会随着活动的减少动态减少,所以用一个入度数组表示每个节点的入度个数
- 初始化入度数组需要遍历图中所有边
判环
拓扑排序检查是否是有向无环图(DAG):
若有向图中存在环,则这个环中的节点的前置活动不可能完成,即节点的入度不可能变为0,所以进入暂存区的节点数量会小于图中的节点总数,所以用计数器count累加出暂存区的节点个数(等于入暂存区的节点个数),最后与图中的节点个数比较
实现
int indegree[MaxNum]; //入度数组
void initIndree(LGraph* graph,int indegree[])[
memset(indegree,0,sizeof(int)*graph->vertexNum);
Edge* p;
for(int i=0;i<graph->vertexNum;i++){ //遍历所有顶点
p=graph->nodes[i]->firstEdge;
while(p){ //遍历所有出度
indegree[p->id]++;
p=p->next;
}
}
]
bool Kahn(LGraph* graph,int indegree[]){
intitIndegree(graph,indegree);
stack<int> s; //保存入度为0的节点的编号
for(int i=0;i<graph->vertexNum;i++){//把初始入度为0的节点入栈
if(indegree[i]==0)s.push(i);
}
int count=0; //出栈的个数
int k;
Edge* p;
while(!s.empty()){
k=s.top();
visit(k); //访问行为
s.pop();
count++;
p=graph->nodes[k]->firstEdge;
while(p){
indegree[p->id]--; //前置活动减少
if(indegree[p->id]==0){
s.push(p->id);
}
p=p->next;
}
}
return count==graph->vertexNum; //返回是否是无环图
}
时间复杂度O(V+E)
逆拓扑排序
记录出度outdegree[],从出度为0的点开始,遍历该点的所有入度,入度节点的出度减一,若减一后为0,存入暂存区,从暂存区取出时输出
DFS算法
dfs回溯的序列与逆拓扑排序相同
- 用dfs遍历图,当返回上层递归时,此时已经遍历过了当前层节点的所有出度(可以认为此时该节点的出度为0),将当前层遍历的节点存入栈(逆拓扑排序)中,最后再将栈边输出边弹出,得到拓扑排序
- 一个节点只能访问一次,需要
visited[]数组记录 - 由于不是强连通图,需要遍历所有节点,对未访问过的节点dfs
判环
由于常规dfs即便是有向有环图也能得到一个错误的逆拓扑排序,所以visited[]数组记录三种状态:0表示未访问过,2表示访问过了该点,且正在访问该点后面的点,1表示该点和该点后面的点都访问过了,即已经得到了该点开始的逆拓扑排序
- 只访问未访问过的点即
visietd不为0的点 - 若要访问的点的
visited为2,说明存在环 - 返回上一层递归时,将
visited标记为1
实现
//链式前向星存图
stack<int> ans; //结果序列
int visited[MaxNum];
bool hasCycle; //是否含有环
void dfs(int x){
visited[x]=2;
int u;
for(int i=head[x];i!=-1;i=edges[i].next){
u=edges[i].to;
if(visited[u]==2){ //存在环
hasCircle=true;
return;
}
else if(visited[u]==0){
dfs(u);
if(hasCircle)return;
}
}
ans.push(x);
visietd[x]=1;
}
void topological(){
for(int i=1;i<=n;i++){
if(visietd[i]==0){
dfs(i);
}
}
if(hasCycle){
cout<<"有环"<<endl;
return;
}
while(!ans.empty()){ //输出拓扑排序
cout<<ans.top()<<" ";
ans.pop(;
}
cout<<endl;
}
时间复杂度O(V+E)

浙公网安备 33010602011771号