拓扑排序

什么是拓扑排序

来自百度百科的解释:对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。

实际上简单的来讲就是在有向图中找到这样一个节点的有序序列,使得对序列中任意一个节点来说,指向该节点的所有边都来自排在它前面的节点。而这样的排序序列根据情况而定可能并不是唯一的。

获得拓扑排序序列的算法

在叙述算法思路之前,首先需要了解入度的概念,一个节点的入度是指其他节点指向这一节点的边的个数。

算法思路: 我们先计算所有节点的入度,将入度为0的节点全部入队,这是开始循环求解之前要进行的准备工作。以队列不为空为循环条件,进项以下循环操作:将队首节点出队并从1开始标上拓扑排序的序号,之后将该节点所指向的节点的入度全部减1,同时查看入度减少的节点的入度是否减少至0,如过入度减少至0,则将该节点也入队。循环重复以上工作直至队列为空停止。

算法实现: 对于图中节点的邻接关系,我们这里使用邻接表来实现,我这里邻接表的实现是采用二级指针创建二维数组的方式来真的创建了一张表,但为了简便并没有动态地分配每一个分数组的大小,实际上去动态的分配可以节省很多的空间。
代码如下:

#include <stdio.h>
#include <stdlib.h>
//定义邻接表
typedef struct AdjacencyList
{
	int n;
	int **list;
 } AList;
 //创建邻接表 通过二维数组的方式 
 AList *Create_AList(int n)
 {
 	int i,j,t;
 	AList * alist=(AList *)malloc(sizeof(AList));
 	alist->n=n;
 	alist->list=(int **)malloc(sizeof(int *)*n);
 	for(i=0;i<n;i++)
 	{
 		alist->list[i]=(int *)malloc(sizeof(int)*n);
	 }
	 printf("\n请输出邻接表:\n");
	 for(i=0;i<n;i++)
	 {
	 	printf("\n请输入第%d个节点的邻接节点:\n",i+1);
	 	for(j=0;j<n;j++)
	 	{
	 		scanf("%d",&t);
	 		if(t!=-1) alist->list[i][j]=t;
	 		else {
	 			alist->list[i][j]=-1;
	 			break;
			 }
		 }
	 }
	 return alist;
 }
 //打印邻接关系 
 void PrintAList(AList * alist)
 {
 	int i=0,j=0,n=alist->n;
 	printf("\n打印邻接关系如下:\n");
 	for(i=0;i<n;i++)
 	{
 		printf("\n第%d个节点可以到达的节点:\n",i+1);
 		for(j=0;j<n;j++)
 		{
 			if(alist->list[i][j]==-1) break;
 			printf("%d ",alist->list[i][j]);
		 }
	 }
 }
 //定义图的节点
 typedef struct vertex
 {
 	int number;
 	int indegree;
  } vertex;
  //计算节点的入度
  void Cindegree(AList *alist,vertex vertexs[])
  {
  	 int i=0,j=0;
  	 for(i=0;i<alist->n;i++) 
	   {
	   	vertexs[i].number=-1;
	   	vertexs[i].indegree=0;
	   }
  	 for(i=0;i<alist->n;i++)
  	 {
  	 	for(j=0;j<alist->n;j++)
  	 	{
  	 		if(alist->list[i][j]==-1) break;
  	 		vertexs[alist->list[i][j]-1].indegree++;
		   }
	   }
	   printf("\n节点的入度:\n");
    for(i=0;i<alist->n;i++) printf("%d ",vertexs[i].indegree);
   } 
 //创建拓扑排序 代码风格还有点乱,需要改进,虽然结果没有问题。。。 
 topSort(AList *alist,vertex vertexs[])
 {
 	int n=alist->n;
	int queue[n],front=0,rear=0,size=0; //用数组定义简易队列 
	int i,j,t,number=0;
 	//先计算所有节点的入度,找到所有入度为0的节点将其入队
	Cindegree(alist,vertexs);
	for(i=0;i<n;i++)
	{
		if(vertexs[i].indegree==0)
		{
			queue[rear++]=i;
			size++;
		}
	}     
		while(size!=0)
		{
			t=queue[front++];
			vertexs[t].number=++number; 
			size--;
			for(j=0;j<n;j++)
			{   
				vertexs[alist->list[t][j]-1].indegree--;
				if(vertexs[alist->list[t][j]-1].indegree==0)
				{
					queue[rear++]=alist->list[t][j]-1;
			        size++;
				}
			}	
		}
  } 
 main()
 {
    int n=7,i;
 	AList * alist=Create_AList(n);
 	PrintAList(alist);
	vertex vertexs[n];
	topSort(alist,vertexs);
	printf("\n拓扑排序的结果为:\n");
	for(i=0;i<n;i++)
	{
		printf("%d ",vertexs[i].number);
	}
 }