在介绍拓扑排序前我们需要了解什么是偏序和全序。
1、偏序——若集合X上的关系R是自反的、反对称的和传递的,则称R是集合X上的偏序关系。
2、全序——设R是集合X上的偏序,如果对每个x,y∈X必有xRy或yRx,则称R是集合X上的全序关系。
而由某个集合上的一个偏序得到该集合上的一个全序,这个操作就称之为拓扑排序。
拓扑排序的经典例子就是选课问题,数据结构书籍上都有介绍,这里不再赘述。那么如何求得一个有向图的拓扑序列呢?对一个有向图,如果图中不存在环,则可以通过以下三步完成拓扑排序:
1、从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它。
2、从网中删去该顶点,并且删去从该顶点发出的全部有向边。
3、重复上述两步,直到剩余的网中不再存在没有前趋的顶点为止。
对于图一中的有向图,由以上方法进行拓扑排序如下:
采用邻接矩阵存储有向图的拓扑排序具体实现如下:
#include <cstdio>
#include <cstdlib>
#define VERTEXNUM 100 //存储顶点数目
typedef char VertexType;
typedef int EdgeType;
typedef int InDegreeType;
/**********************************************
*
* 邻接矩阵存储结构
*
**********************************************/
typedef struct
{
VertexType vertexs[VERTEXNUM]; //顶点表
InDegreeType indegree[VERTEXNUM]; //入度
EdgeType edges[VERTEXNUM][VERTEXNUM]; //邻接矩阵存储顶点间关系
int vernum, edgenum; //图中当前的顶点和边数
}Graph;
int topsort[VERTEXNUM]; //保存拓扑排序后顶点序号
/**********************************************
*
* 建立邻接矩阵
*
**********************************************/
void MakeGraph(Graph *&graph)
{
int i, j, k;
printf("请输入图的顶点数n和边数e:\n");
scanf("%d%d", &graph->vernum, &graph->edgenum);
printf("请输入顶点信息(顶点号<CR>)每个顶点以回车作为结束:\n");
for(i = 0; i < graph->vernum; i++)
{
getchar();
scanf("%c", &graph->vertexs[i]);
}
for(i = 0; i < graph->vernum; i++) //边信息初始化
{
for(j = 0; j < graph->vernum; j++)
{
graph->edges[i][j] = 0;
}
}
printf("请输入每条边对应的两个顶点的序号(格式为i,j):\n");
for(k = 0; k < graph->edgenum; k++)
{
scanf("%d,%d", &i, &j);
graph->edges[i - 1][j - 1] = 1;
}
}
/**********************************************
*
* 计算图中各顶点的入度
*
**********************************************/
void GetInDegree(Graph *graph)
{
int i, j;
for(i = 0; i < graph->vernum; i++)
graph->indegree[i] = 0;
for(i = 0; i < graph->vernum; i++) //计算图中各顶点的入度
{
for(j = 0; j < graph->vernum; j++)
{
if(graph->edges[i][j] == 1)
{
graph->indegree[j]++;
}
}
}
}
/**********************************************
*
* 拓扑排序
*
**********************************************/
bool TopLogicalSort(Graph *graph)
{
int i, j, k;
for(i = 0; i < graph->vernum; i++)
{
j = 0;
while(graph->indegree[j] != 0)
{
j++;
if(j > graph->vernum)
return false;
}
topsort[i] = j;
graph->indegree[j] = -1; //设置已拓扑排序顶点的入度为-1
for(k = 0; k < graph->vernum; k++)
{
if(graph->edges[j][k] == 1)
graph->indegree[k]--; //对j号顶点的每个邻接点的入度减1
}
}
return true;
}
int main()
{
Graph *graph = (Graph *)malloc(sizeof(Graph));
MakeGraph(graph);
GetInDegree(graph);
if(TopLogicalSort(graph))
{
printf("\n拓扑排序序列为:\n");
for(int i = 0; i < graph->vernum; i++)
{
printf("%c ", graph->vertexs[topsort[i]]);
}
printf("\n");
}
else
{
printf("图中存在环\n");
}
return 0;
}
以上代码测试结果如下图:
代码中如存在问题,望指正。