说明
拓扑排序,其实就是对一个有向图构造拓扑序列的过程。
如果构造的排序将全部顶点都输出了则说明是一个不存在环的AOV网,如果输出点少于顶点数,则说明存在环,则构不成拓扑排序。
在构成的排序线性顺序中,排在前面的通常都是图中的前置点,比如图中顶点的方向是从顶点4->6 那么在拓扑序列中,4一定会排在6的前面。
原理
通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。离散数学中关于偏序和全序的定义:
若集合X上的关系是R是自反的、反对称的和传递的,则称R是集合X上的偏序关系。 设R是集合X上的偏序(Partial Order),如果对每个x,y属于X必有xRy 或 yRx,则称R是集合X上的全序关系。 注意: ①若将图中顶点按拓扑次序排成一行,则图中所有的有向边均是从左指向右的。 ②若图中存在有向环,则不可能使顶点满足拓扑次序。③一个DAG的拓扑序列通常表示某种方案切实可行。
应用
(1)在Visual Studio .NET中,一个解决方案可以包含多个项目,一个项目可以引用若干其它项目。编译的时候,VS会自动确定每个项目的编译顺序。VS究竟是如何计算出这个顺序的呢?
(2)给定字母表的前n个大些字母,以及这些字母间两两之间的大小关系(这样的关系给定m组),问由这m组关系能否确定n个字母的整体顺序,如果能输出按续排列的字母。
算法思想
简单的实现步骤如下
(1)从有向图中选择一个入度为0的顶点并且输出它.
(2)从网中删去该顶点,并且删去从该顶点发出的全部有向边.
(3)重复上述两步,直到剩余的网中不再存在没有前趋的顶点为止.
对于这样的AOV网用邻接表的结构(同时为顶点添加一个入度的成员)来存储顶点比较方便来处理,
添加一个栈,初始化将所有入度为0的顶点加入到栈,添加一个输出计数;
再进行一个循环判断栈是否为空,在循环内部出栈一个顶点并输出,同时输出计数加一,删除以此顶点为尾的弧,也就是讲邻接点的入度减一,同时判断此顶点的入度是否为0,为0则添加到栈中;
循环执行完后,判断输出计数与顶点的关系,如果输出计数小于顶点,则说明有环,返回错误,拓扑排序失败。
代码实现
#define MAXVEX 20
using namespace std;
typedef struct EdgeNode //边表结点
{
int adjvertex; //邻接点域,存储该顶点对应的下标
EdgeNode* next;
};
typedef struct Vertex
{
int data;
int in; //存放顶点入度
EdgeNode* firstVertex; //边表头指针
}AdjList[MAXVEX];
typedef struct GraphAdjList
{
AdjList adjList;
int numVertexes,numEdges;
};
int TopologicalSort(GraphAdjList* GL)
{
int *stack = (int*)malloc(sizeof(int)*GL->numVertexes);
int count =0,top=0;
for(int i=0; i < GL->numVertexes; ++i)
{
if( !(GL->adjList[i].in) )
stack[++top]= i;
}
while(top)
{
int getTop = stack[top--];
cout<<GL->adjList[getTop].data<<endl;
++count;
for(EdgeNode* edgeNode = GL->adjList[getTop].firstVertex;
edgeNode;
edgeNode=edgeNode->next)
{
if( !(--GL->adjList[edgeNode->adjvertex].in) )
stack[++top]= edgeNode->adjvertex;
}
}
if(count < GL->numVertexes)
return 1; //error
return 0;// right
}