基于邻接表的拓扑排序和基于DFS的逆拓扑排序

目录

基于邻接表的拓扑排序

基本思想

具体代码

基于DFS的逆拓扑排序

基本思想

具体代码

补充概念


基于邻接表的拓扑排序

基本思想

        遍历indegree[]数组找到入度为0的点,由于这种点可能不止一个,所以需要用容器存起来(栈和队列都可以),然后每次从容器中删除一个入度为0的点,并且删除和该点邻接的边(和该点邻接的点的indegree[]数值-1),直到图的所有顶点都被删除,删除点的顺序就是拓扑排序,但是如果有点没有被删除,说明该图存在环。

具体代码
#include<bits/stdc++.h>
#include<stack> 

using namespace std;

const int MAXSIZE=100010;
const int N=100010;

int indegree[N];  //记录每个点的入度 

typedef struct ANode
{
	int data;
	struct ANode *nextarc;
}ANode;                  //边 

typedef struct VNode
{
	int data;
	ANode *firstarc;
}VNode,AdjList[MAXSIZE];  //点表 

typedef struct
{
	AdjList vertices;
	int vexnum;
	int arcnum;
}ALGraph;           //邻接表 

ALGraph  Q;
stack<int> s;  //用栈存放顶点 
int cnt;
int print[N];

bool TopoSort()
{
	for(int i=1;i<=Q.vexnum;i++)
	{
		if(indegree[i]==0)
		{
			s.push(i);
		}
	}
	while(!s.empty())
	{
		int q=s.top();
		s.pop();
		print[++cnt]=q;  //思路很好---暂存拓扑排序 
		for(ANode* t=Q.vertices[q].firstarc;t;t=t->nextarc)
		{
			if(!(--indegree[t->data])) s.push(t->data);   //如果入度为0,则加入到栈中 
		}
	}
	if(cnt<Q.vexnum) return false;
	return true;
}

int main()
{
	freopen("A.txt","r",stdin); 
	scanf("%d%d",&Q.vexnum,&Q.arcnum);
	for(int i=1;i<=Q.vexnum;i++)  //初始化
	{
		Q.vertices[i].data=i;
		Q.vertices[i].firstarc=NULL;   //将所有的边表初始化为NULL 
	} 
	for(int i=1;i<=Q.arcnum;i++)  //邻接表的初始化 
	{
		int a,b;
		scanf("%d%d",&a,&b);
		ANode *newedge=new ANode;
		newedge->data=b;
		newedge->nextarc=Q.vertices[a].firstarc;
		Q.vertices[a].firstarc=newedge;            //头插法将边插入 
		indegree[b]++;   //更新入度 
	}
	
//	for(int i=1;i<=Q.vexnum;i++)
//	{
//		cout<<indegree[i]<<" ";
//	}
//	cout<<endl;
	
	if(!TopoSort())
	{
		cout<<"该图存在环"; 
	}
	else
	{
		for(int i=1;i<=Q.vexnum;i++)
		{
			cout<<print[i]<<" ";
		}
	}
	return 0;
} 

基于DFS的逆拓扑排序

基本思想

        DFS遍历时,祖先节点的访问一定在孩子节点之后,所以在DFS的每次递归退出之前用一个变量来记录该顶点是第几个被访问的,这样就可以得到逆拓扑排序序列。

        注意,逆拓扑排序序列和拓扑排序序列不是单纯的翻转关系。另外,代码中用st[]来辅助判断这个有向图中是否存在环,如果有环,则不存在拓扑排序和逆拓扑排序

具体代码
#include<bits/stdc++.h>

using namespace std;

const int MAXSIZE=100010;
const int N=100010;

int indegree[N];  //记录每个点的入度 

typedef struct ANode
{
	int data;
	struct ANode *nextarc;
}ANode;                  //边 

typedef struct VNode
{
	int data;
	ANode *firstarc;
}VNode,AdjList[MAXSIZE];  //点表 

typedef struct
{
	AdjList vertices;
	int vexnum;
	int arcnum;
}ALGraph;           //邻接表 

ALGraph  Q;
stack<int> s;  //用栈存放顶点 
int cnt;
int print[N];
int st[N];
int res;

/*
  0:代表未访问
 -1:代表访问完毕
  1:代表是这一阶段正在访问的(这一阶段指的是两个元素在同一个递归中)。
*/

bool DFS(int x)
{
	st[x]=1;
	for(ANode* i=Q.vertices[x].firstarc;i;i=i->nextarc)
	{
		//如果遇到了正在访问的结点,那么说明有环
		if(st[i->data]==1) return true;
		//如果v这个结点没有访问过,递归查找v结点是否在环中
		else if(st[i->data]==0&&DFS(i->data))  return true;
	}
	st[x]=-1;
	print[++cnt]=x;
	return false;
}

int main()
{
	freopen("B.txt","r",stdin); 
	scanf("%d%d",&Q.vexnum,&Q.arcnum);
	for(int i=1;i<=Q.vexnum;i++)  //初始化
	{
		Q.vertices[i].data=i;
		Q.vertices[i].firstarc=NULL;   //将所有的边表初始化为NULL 
	} 
	for(int i=1;i<=Q.arcnum;i++)  //邻接表的初始化 
	{
		int a,b;
		scanf("%d%d",&a,&b);
		ANode *newedge=new ANode;
		newedge->data=b;
		newedge->nextarc=Q.vertices[a].firstarc;
		Q.vertices[a].firstarc=newedge;            //头插法将边插入 
	}
	
	for(int i=1;i<=Q.vexnum;i++)
	{
		if(!st[i]) 
		{
			DFS(i);
		}
	}
	
	for(int i=1;i<=Q.vexnum;i++)
	{
		cout<<print[i]<<" ";
	}

	return 0;
} 

补充概念

        有向图强连通(考虑边的方向方向)必定有环,无向图连通可以没有环(树)。

        有向图中的强连通分量必定有环,另外,有向图中单个顶点也是强连通分量,如果一个有向图的顶点不能排成一个拓扑序列,则这个有向图一定含有顶点数大于1的强连通分量。如果是无向图,则一定含有环。

        判断一个图是否有环的方法:拓扑排序,DFS。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值