拓扑排序
对一个有向无环图(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],数据的元素填每个顶点的入度情况。
先用数组的下标代替顶点。
利用数组栈遍历顶点
第一步 : 4 , 1 入栈
第二步: 4 , 1 出栈
第三步 :2 入栈
第四步: 2 出栈
第五步 : 3, 5 入栈
第六步: 3 , 5 出栈
拓扑排序:
//使用数组栈实现拓扑排序
void TopoSort(ALGraph* G)
{
int i, getTop,j, k, top, count = 0;
EdgeNode* p;
int stack[20]; //入度个数
int* list_in = (int*)malloc((G->n)*sizeof(int)); //数组栈
for (i = 1;i <= G->n;i++) //初始化数组
{
list_in[i] = 0;
}
for (i = 1;i <= G->n;i++) //统计各个顶点的入度情况,并把他们填入数组里面
{
p = G->adjlist[i].firstedge;
while (p != NULL)
{
j = p->adjvex; //某个顶点的出度也就相当于该顶点的出度
list_in[j]++; //该顶点的入度加1
p = p->next;
}
}
top = 0;
for (i = 1;i <= G->n; i++) //先找出里面入度是0的顶点
{
if (list_in[i] == 0)
{
stack[++top] = i; //寻找入度为0的顶点,在进栈
}
}
while (top != 0)
{
getTop = stack[top--]; //取出栈顶元素,出栈
printf("%d ", G->adjlist[getTop].vertex);
count++; //统计顶点
p = G->adjlist[getTop].firstedge;
while (p)
{
k = p->adjvex; //相l连接的顶点
list_in[k]--; //相连接的顶点入度减1
if (list_in[k] == 0) //如果发现入度为0的新顶点,该顶点进栈
{
stack[++top] = k;
}
p = p->next;
}
}
if (count < G->n) printf("\n有回路!\n");
}
邻接表建立
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 i;
for (i = 1;i <= G->n ;i++)
{
if (G->adjlist[i].vertex == e)
{
return i;
}
}
return -1;
}
void create(ALGraph* G) //创建邻接表
{
int i, j, k;
EdgeNode* s;
printf("读入顶点和边数");
scanf_s("%d %d", &G->n, &G->e);
for (i = 1;i <= G->n;i++)
{
printf("建立顶点表");
fflush(stdin);
scanf_s("%d", &G->adjlist[i].vertex);
G->adjlist[i].firstedge = NULL;
}
printf("建立边表\n");
for (k = 1;k <= G->e;k++)
{
printf("读入(vi-vj)的顶点对序号");
scanf_s("%d %d", &i, &j);
i = FindVertex(G, i);
j = FindVertex(G, j);
while (i == -1 || j == -1)
{
printf("找不到顶点,请重新输入!\n");
printf("读入(vi-vj)的顶点对序号");
scanf_s("%d %d", &i, &j);
i = FindVertex(G, i);
j = FindVertex(G, j);
}
s = (EdgeNode*)malloc(sizeof(EdgeNode));
s->adjvex = j;
//头插
s->next = G->adjlist[i].firstedge;
G->adjlist[i].firstedge = s;
}
}
//打印邻接表
void PrintALG(ALGraph* G)
{
EdgeNode* p;
int i;
printf("其邻接表为('->'表示两个之间有连接):\n");
for (i = 1;i <= G->n;i++)
{
p = G->adjlist[i].firstedge;
printf("%d", G->adjlist[i].vertex);
if (G->adjlist[i].firstedge != NULL)
{ //若该顶点没有出度则不打印连接符
printf("->");
}
while (p != NULL)
{
printf("%d", G->adjlist[p->adjvex].vertex);
if (p->next != NULL) //取消最后一个节点的连接符
{
printf("->");
}
p = p->next;
}
printf("\n");
}
}
完整代码
#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 i;
for (i = 1;i <= G->n ;i++)
{
if (G->adjlist[i].vertex == e)
{
return i;
}
}
return -1;
}
void create(ALGraph* G) //创建邻接表
{
int i, j, k;
EdgeNode* s;
printf("读入顶点和边数");
scanf_s("%d %d", &G->n, &G->e);
for (i = 1;i <= G->n;i++)
{
printf("建立顶点表");
fflush(stdin);
scanf_s("%d", &G->adjlist[i].vertex);
G->adjlist[i].firstedge = NULL;
}
printf("建立边表\n");
for (k = 1;k <= G->e;k++)
{
printf("读入(vi-vj)的顶点对序号");
scanf_s("%d %d", &i, &j);
i = FindVertex(G, i);
j = FindVertex(G, j);
while (i == -1 || j == -1)
{
printf("找不到顶点,请重新输入!\n");
printf("读入(vi-vj)的顶点对序号");
scanf_s("%d %d", &i, &j);
i = FindVertex(G, i);
j = FindVertex(G, j);
}
s = (EdgeNode*)malloc(sizeof(EdgeNode));
s->adjvex = j;
//头插
s->next = G->adjlist[i].firstedge;
G->adjlist[i].firstedge = s;
}
}
//打印邻接表
void PrintALG(ALGraph* G)
{
EdgeNode* p;
int i;
printf("其邻接表为('->'表示两个之间有连接):\n");
for (i = 1;i <= G->n;i++)
{
p = G->adjlist[i].firstedge;
printf("%d", G->adjlist[i].vertex);
if (G->adjlist[i].firstedge != NULL)
{ //若该顶点没有出度则不打印连接符
printf("->");
}
while (p != NULL)
{
printf("%d", G->adjlist[p->adjvex].vertex);
if (p->next != NULL) //取消最后一个节点的连接符
{
printf("->");
}
p = p->next;
}
printf("\n");
}
}
//使用数组栈实现拓扑排序
void TopoSort(ALGraph* G)
{
int i, getTop,j, k, top, count = 0;
EdgeNode* p;
int stack[20]; //入度个数
int* list_in = (int*)malloc((G->n)*sizeof(int)); //数组栈
for (i = 1;i <= G->n;i++) //初始化数组
{
list_in[i] = 0;
}
for (i = 1;i <= G->n;i++) //统计各个顶点的入度情况,并把他们填入数组里面
{
p = G->adjlist[i].firstedge;
while (p != NULL)
{
j = p->adjvex; //某个顶点的出度也就相当于该顶点的出度
list_in[j]++; //该顶点的入度加1
p = p->next;
}
}
top = 0;
for (i = 1;i <= G->n; i++) //先找出里面入度是0的顶点
{
if (list_in[i] == 0)
{
stack[++top] = i; //寻找入度为0的顶点,在进栈
}
}
while (top != 0)
{
getTop = stack[top--]; //取出栈顶元素,出栈
printf("%d ", G->adjlist[getTop].vertex);
count++; //统计顶点
p = G->adjlist[getTop].firstedge;
while (p)
{
k = p->adjvex; //相l连接的顶点
list_in[k]--; //相连接的顶点入度减1
if (list_in[k] == 0) //如果发现入度为0的新顶点,该顶点进栈
{
stack[++top] = k;
}
p = p->next;
}
}
if (count < G->n) printf("\n有回路!\n");
}
int main()
{
ALGraph* G = (ALGraph*)malloc(sizeof(ALGraph));
create(G);
PrintALG(G);
printf("拓扑排序为:\n");
TopoSort(G);
getchar();
return 0;
}