学习图论(二)——拓扑排序

拓扑排序

参考博客:https://blog.csdn.net/jasmine_shine/article/details/43488895;

什么是拓扑排序?
在图论中,拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。
该序列必须满足下面两个条件:
1.每个顶点出现且只出现一次。
2.若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。
有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序。
用自己的话说,就是把有向无环图中的点按照图中边的方向排序,在边前端的节点先出现,在边后端的节点后出现。

注意:拓扑排序的结果有时并不唯一

先说几个简单概念:
1.前驱(pre):一个点(V)如果与另外一个点(U)相连,且U在有向边(U,V)的前端,则称V存在前驱。
2.度(degree):一个节点的度指的是与该节点相关联的边的条数。对有向图来说,一个点的度可以分为入度和出度。
①入度:与该点相关联的边中以该点为终点的边的条数。
②出度:与该点相关联的边中以该点为起点的边的条数。
3.偏序:有向图中存在两个点,它们之间的关系是不明确的。即两个点不知谁在前谁在后。
4.全序:有向无环图中任意两个顶点的关系都是明确的(谁前谁后或者并排)。

拓扑排序的作用:
引用从学习过程中看到的一段话(出处没有保留,向该话博主致歉):
拓扑排序通常用来“排序”具有依赖关系的任务。
比如,如果用一个DAG图来表示一个工程,其中每个顶点表示工程中的一个任务,用有向边 (A,B)表示在做任务 B 之前必须先完成任务 A。故在这个工程中,任意两个任务要么具有确定的先后关系,要么是没有关系,绝对不存在互相矛盾的关系(即环路)。

也就是用来确定有向图中各个点的先后关系。

常用的方法如下(BFS思想实现):
从 DAG 图中选择一个没有前驱(即入度为0)的顶点并输出。
从图中删除该顶点和所有以它为起点的有向边。
重复 1 和 2 直到当前的 DAG 图为空或当前图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。
若存在环,则输出的节点小于总节点数。所以可以用一个计数器,记录“删掉”的环的总数,再与总节点数比较,看是否存在环。

代码:
以下代码为参考博客中的代码(BFS思想),其中添加了本人学习时的注释。

#include
#include
#include
#include
using namespace std;
#define MAX 9999

queuemyqueue;
int indegree[MAX];

struct node
{
	int adjvex;//当前点 
	node* next; //往下的相邻的点 
} adj[MAX];

int Create(node adj[],int n,int m)//邻接表建表函数,n代表定点数,m代表边数
{
	int i;
	node *p;
	for(i=1; i<=n; i++)
	{

		adj[i].adjvex=i;
		adj[i].next=NULL;
	}
	for(i=1; i<=m; i++)
	{
		cout<<"请输入第"<>u>>v;
		//下面链表的连接相当于用vector存储 u的相邻点
		//相当于push_front()的操作 
		p=new node;
		p->adjvex=v;
		p->next=adj[u].next;
		adj[u].next=p;
	}
	return 1;//创建成功 
}

void print(int n)//邻接表打印函数
{
	int i;
	node *p;
	for(i=1; i<=n; i++)
	{
		p=&adj[i];
		while(p!=NULL)
		{
			cout<adjvex<<' ';
			p=p->next;
		}
		cout<adjvex]++;//计算每一个点的入度 
			p=p->next;
		}
	}
	for(i=1; i<=n; i++)
	{
		//寻找没有前驱的节点为起始节点 
		if(indegree[i]==0)
			myqueue.push(i);
	}
	int count=0;//计数器,用于判断是否有环 
	//BFS思想 
	while(myqueue.size()!=0)
	{

		i=myqueue.front();
		myqueue.pop();
		//输出拓扑排序,因为只有没有前驱的点才可以入队,所以 队首元素肯定是符合条件的元素 
		cout<next)
		{
			int k=p->adjvex;
			indegree[k]--;//相当于删掉两个点之间的边 
			//寻找没有前驱的点 
			if(indegree[k]==0)
				myqueue.push(k);
		}
	}
	cout<>n>>m;
	Create(adj,n,m);
	cout<<"输入的邻接表为:"<

下面是学习后自己写的代码,也是基于BFS

#include
using namespace std;
const int MAXN = 1e3;
vector storage;//存储排序后的节点 
int indegree[MAXN]; //入度。 也可以声明为动态数组,比较灵活,不过访问时比较慢 

void print()
{
	for(int i=0;i v[],int n)
{
	queue q;
	//寻找第一个点,即没有前驱的点 
	for(int i=1; i<=n; ++i)
		if(!indegree[i])
			q.push(i);
	int cnt = 0; //计数器,判断是否存在回路 
	while(!q.empty())
	{
		int top = q.front();
		q.pop();
		cnt++;
		storage.push_back(top);//所有符合条件入队的点都是排序好了的点 
		for(int i=0;i>n>>m;
		if(!n)
			break;
		vector *V = new vector[n+1] ; //用vector 存储边的对应关系 
		memset(indegree,0,sizeof(indegree));
		storage.clear();
		for(int i=1; i<=m; ++i)
		{
			cout<<"请输入第"<>u>>v;
			V[u].push_back(v);// 存储边 U->V 
			indegree[v]++;// 入度增加 
		}
		topological_sort(V , n);
		for(int i=0;i<=n;++i)
			V[i].clear();
		delete []V;	
	}
}

拓扑排序也可以是基于DFS的,但我暂时没学到。等学会后再补充。

你可能感兴趣的:(图论)