对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
拿个例子来说,比如下面一张图:
上面的图中的1的顶点的入度为0,先去掉1顶点,去掉1顶点进入其他顶点的边(去掉1->2,去掉1->3),这时2和3d的顶点的入度
变为0,去掉2顶点,去掉(2->4)边,去掉3顶点,去掉(3->4)边,注意到4这个顶点的时候就出现了环,而拓扑排序计算的是有向无环图,有环的话是计算不出拓扑排序的。就好像是4代表C语言课程,4是离散数学,6是数据结构,咱们先认定先学C语言课程和离散数学才能学数据结构,像下面的图那样。
如果反过的话就违背之前的规定,打乱了顺序而导致不知道先学那一科。
如果改成上面的图的话,就可以计算出拓扑排序为1 2 3 4 5 6。
这个时候你差不多知道拓扑排序的原理是什么了,但如何去计算拓扑排序呢?好,下面我再举个例子为大家讲解一下。
首先我们要创建邻接表:
也可以参考我的邻接表博客:
邻接表完整代码
接着我们要定义一个一维数组作为栈来使用,上面有6个顶点,我们可以a[6],数据的元素填每个顶点的入度情况。
先用数组的下标代替顶点。
在定义一把 top 指针,初始化top=-1,
先找到第一个是入度是0的元素,赋值为-1,如果还有入度为0的元素,注意赋的值为之前的入度为0的元素的下标,比如之前入度的元素的下标为1,则a[4]=1,这样的话就把入度的0的顶点形成一条链,以上图为例刚开始时top=4,删除4顶点以及4顶点进入其他顶点的边,如果删除4顶点后出现入度为0的顶点,假设为b顶点,则接着从b顶点再出发,如果没有的话,top=a[top],则top变为另一个顶点,为1顶点,从1顶点出发。
完整代码如下:
#include<stdio.h>
#include<stdlib.h>
#define MaxVertexNum 100
#define ERROR 0
#define OK 1
#define FALSE 0
#define TRUE 1
typedef int Boolean;
typedef int VertexType;
Boolean visit[MaxVertexNum];
typedef struct node
{
int adjvex;
struct node *next;
}EdgeNode;
typedef struct
{
VertexType vertex;
EdgeNode *firstedge;
}VertexNode;
typedef VertexNode AdjList[MaxVertexNum];
typedef struct
{
AdjList adjlist;
int n,e;
}ALGraph;
int FindVertex(ALGraph *G ,int e,int n)
{
int i;
for(i=0;i<n;i++)
{
if(G->adjlist[i].vertex==e)
{
return i;
}
}
return -1;
}
void create(ALGraph *G) //创建邻接表
{
int i,j,k,w,v;
EdgeNode *s;
printf("读入顶点和边数");
scanf("%d %d",&G->n,&G->e);
for(i=0;i<G->n;i++)
{
printf("建立顶点表");
fflush(stdin);
scanf("%d",&G->adjlist[i].vertex);
G->adjlist[i].firstedge=NULL;
}
printf("建立边表\n");
for(k=0;k<G->e;k++)
{
printf("读入(vi-vj)的顶点对序号");
scanf("%d %d",&i,&j);
i=FindVertex(G,i,G->n);
j=FindVertex(G,j,G->n);
if(i==-1||j==-1)
{
printf("找不到顶点,请重新输入!\n");
printf("读入(vi-vj)的顶点对序号");
scanf("%d %d",&i,&j);
i=FindVertex(G,i,G->n);
j=FindVertex(G,j,G->n);
}
s=(EdgeNode*)malloc(sizeof(EdgeNode));
s->adjvex=(j);
s->next=G->adjlist[i].firstedge;
G->adjlist[i].firstedge=s;
}
}
void TopoSort(ALGraph *G,int n)
{
int i,j,k,top,m=0;
EdgeNode *p;
int *d=(int *)malloc(n*sizeof(int));
for(i=0;i<n;i++) //初始化数组
{
d[i]=0;
}
for(i=0;i<n;i++) //统计各个顶点的入度情况,并把他们填入数组里面
{
p=G->adjlist[i].firstedge;
while(p!=NULL)
{
j=p->adjvex;
d[j]++;
p=p->next;
}
}
top=-1;
for(i=0;i<n;i++) //先找出里面入度是0的顶点
{
if(d[i]==0)
{
d[i]=top;
top=i;
}
}
while(top!=-1)
{
j=top;
top=d[top];
printf("%d ",j);
m++; //统计顶点
p=G->adjlist[j].firstedge;
while(p)
{
k=p->adjvex; //相l连接的顶点
d[k]--; //相连接的顶点入度减1
if(d[k]==0) //如果发现入度为0的新顶点,从该顶点出发
{
d[k]=top;
top=k;
}
p=p->next;
}
}
if(m<n) printf("\n有回路!\n");
free(d);
}
void main()
{
ALGraph *G=(ALGraph *)malloc(sizeof(ALGraph));
EdgeNode *p;
create(G);
int i;
printf("其邻接表为('->'表示两个之间有连接):\n");
for(i=0;i<G->n;i++)
{
p=G->adjlist[i].firstedge;
printf("%d->",G->adjlist[i].vertex);
while(p!=NULL)
{
printf("%d->",G->adjlist[p->adjvex].vertex);
p=p->next;
}
printf("\n");
}
printf("拓扑排序为:\n");
TopoSort(G,G->n);
}
初始化数据:
结果:
说明一下,拓扑排序可能有多种结果。
好了,拓扑排序就介绍到这里了。
谢谢你的浏览,创作不易,点个赞再走呗,支持一下作者!谢谢啦!