图的拓扑排序
图的拓扑排序理解起来很简单,一个简单易懂的定义:将有向图中的顶点以线性方式进行排序。即对于任何连接自顶点u到顶点v的有向边<u,v>,在最后的排序结果中,顶点u总是在顶点v的前面。
介绍图的拓扑排序之前需要介绍偏序和全序的概念,在这里我只说说我的理解。
我们规定有向图入度为零的顶点才可以被访问,则图a中的部分顶点存在着先后关系,很明显V2和V3不存在先后关系,它们之间不可以比较,这种关系为偏序。而图b中每一个顶点都可以比较,这种关系为全序关系。
如果一个图为全序关系,我们可以简单的从入度为零的顶点顺着边的方向挨个访问顶点即可。而我们需要解决的是具有偏序关系的图,当我们人为的在V2和V3之间加上一个V2<=V3的弧,则图a表示的亦为全序,且这个全序称为拓扑有序,而由偏序定义得到拓扑有序的操作便是拓扑排序。
拓扑排序的实现方法有很多种,可以使用队列或是栈从入度角度或是出度的角度出发,或者简单的使用一个数组存放顶点的访问顺序也可以。
在这里我从顶点的入度出发。首先有向图,使用邻接表来存储,邻接表的头结点有两个数据域一个用来存储顶点的入度,一个为指向表结点的指针。表结点和头节点公用一个结构,只是存储入度的数据域改为用来存储头节点所指结点在图中的序号。
typedef struct vertex{
int iInDegree; //头结点中表示顶点的入度,表结点中表示顶点的序号
struct vertex * pNext;
}VERTEX;
typedef struct {
VERTEX * pVertex;
int iVexNum;
}GRAPH;
以上图为例,我们做它的拓扑排序。
算法流程:
实现代码:
#include <stdio.h>
#include <malloc.h>
#define MAX_VEX_NUN 100
typedef struct vertex{
int iInDegree; //头结点中表示顶点的入度,表结点中表示顶点的序号
struct vertex * pNext;
}VERTEX;
typedef struct {
VERTEX * pVertex;
int iVexNum;
}GRAPH;
void CreateGraph(GRAPH * pGraph)
{
int iVexNum;
int iLinkNum;
int iVexOrder;
VERTEX * pNext;
printf("输入顶点数量:");
scanf_s("%d", &iVexNum);
pGraph->pVertex = (VERTEX *)malloc(sizeof(VERTEX)*iVexNum);
pGraph->iVexNum = iVexNum;
for (int i = 0;i < iVexNum;i++)
{
pGraph->pVertex[i].iInDegree = 0;
pGraph->pVertex[i].pNext = NULL;
}
for (int i = 0;i < iVexNum;i++)
{
printf("输入第%d个顶点指向其他顶点的数量:", i + 1);
scanf_s("%d", &iLinkNum);
if (iLinkNum != 0)
{
printf("输入这些被指向顶点的序号:");
for (int j = 0;j < iLinkNum;j++)
{
scanf_s("%d", &iVexOrder);
pNext = (VERTEX *)malloc(sizeof(VERTEX));
pNext->iInDegree = iVexOrder - 1;
pGraph->pVertex[iVexOrder - 1].iInDegree++;
pNext->pNext = pGraph->pVertex[i].pNext;
pGraph->pVertex[i].pNext = pNext;
}
}
}
}
int TopSort(GRAPH * pGraph,int * pSorted)
{
int iVexCnt = 0;
int iVisitedCnt = 0;
VERTEX * pNext;
for (int i = 0;i < pGraph->iVexNum;i++)
{
if (pGraph->pVertex[i].iInDegree == 0)
pSorted[iVexCnt++] = i;
}
while (iVisitedCnt != iVexCnt)
{
pNext = pGraph->pVertex[pSorted[iVisitedCnt++]].pNext;
for (;pNext != NULL;pNext = pNext->pNext)
{
if ((--pGraph->pVertex[pNext->iInDegree].iInDegree) == 0)
pSorted[iVexCnt++] = pNext->iInDegree;
}
}
return iVexCnt;
}
int main(void)
{
GRAPH Graph;
int Sorted[MAX_VEX_NUN];
CreateGraph(&Graph);
if (Graph.iVexNum == TopSort(&Graph, Sorted))
for (int i=0;i < Graph.iVexNum;i++)
printf("%3d", Sorted[i]+1);
else
printf("此图有回路,无法进行拓扑排序。");
return 0;
}
TopSort()函数返回找到的入度为零的顶点的个数,通过比较返回的个数和图的所有顶点个数,可以判断这张图是否为有环图。
以上图为例的输出结果:
平台:window10 vs2015
完整工程代码下载: