拓扑排序

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)

posted @ 2026-04-11 20:05  mofei1116  阅读(2)  评论(0)    收藏  举报