拓扑排序属于有向无环图的应用,在实际工作中,长长把用一个有向图来表示工程的施工流程图,或者产品生产的流程图。一个工程一般可以分为若干个子工程,通常把子工程称为“活动”。有些应用关心工程是否能够顺利完成,即是否存在一个从工程开始到工程完成的活动序列,该序列满足所有活动之间的制约关系(一些活动的开始必须以另一个活动的结束为条件);而有些关系的是,工程的完成时间的估算,哪些活动是影响工程进度的关键。对于两种不同的要求有向图提供了两类支持:一个是拓扑序列和逆拓扑序列,第二个就是关键路径。
第一个拓扑序列是以顶点为活动的有向图(成AOV网)为基础,在AOV网中一定不能存在回路,构造AOV网的拓扑有序序列的运算称为拓扑排序(完全不同于传统的键值排序)排序后的序列即为拓扑序列。
基本思想如下: 在AOV网中至少存在有一个入度为0的点(没有前驱的顶点,没有被制约的定点),首先找到一个入度为0的顶点输出他,并删除它所发出的所有有向边(这样做可能会产生新的入度为0的点);继续寻诈骗下一个入度为0的顶点进行输出,并删除它所发出的所有的有向边;....;重复上述动作知道再也找不到一个入度为0的顶点为止。
得到两种结果:1 若拓扑有序序列包含了AOV网所有的顶点(AOV网的所有顶点均被输出),则说明AOV网中不存在有向回路(拓扑排序成功);
2 拖拓扑有序序列没有包含AOV网所有的顶点(AOV网中的顶点没有全部输出,剩余的顶点均有前驱顶点),则说明AOV网中存在有向回路(排序失败)。
注:拓扑序列并不是唯一的,同一组活动制约不同的方法可以得到不同的拓扑序列
下面是基本的实现代码,建议自己亲手尝试下肯定会有很多的收获,因为这个程序的数据结构比较复杂,需要注意的地方很多。
/*
拓扑排序:基于邻接表的拓扑排序
作者:chongge
*/
#include <iostream>
const int MAX = 100;
typedef char VertexType;
typedef int ElemType;
typedef struct EdgeNode
{
int adjno; //后继链表
struct EdgeNode *next;
}EdgeNode;
typedef struct VertexNode //邻接表元素
{
VertexType data; //顶点标识
EdgeNode *first; //后继链表起点
}VertexNode;
typedef struct
{
VertexNode vexs[MAX]; //邻接表数组
int vexnum; //记录图的定点数
int arcnum; //弧的条数
}AdList;
/*求AOV网的各顶点的入度*/
void GetInDegree(AdList *AOV,int indegree[])
{
EdgeNode *p= NULL;
for(int i= 1 ;i <=AOV->vexnum ; i++)
{
p = AOV->vexs[i].first;
while(p != NULL)
{
indegree[p->adjno] ++ ;
p = p->next;
}
}
}
int TopSort(AdList *AOV)
{
int stack[MAX];
int top = 0 ; //作为简易栈使用
EdgeNode *p = NULL;
int indegree[MAX];
for(int i=1; i<=AOV->vexnum; i++)
indegree[i] = 0;
GetInDegree(AOV,indegree);
for(int i=1; i<=AOV->vexnum ;i++)
if(indegree[i] == 0)
stack[++ top] = i;
int cnt =0;
while(top > 0)
{
int i = stack[top --];
std::cout << i << " ";
cnt ++;
p = AOV->vexs[i].first;
while(p != NULL)
{
indegree[p->adjno] --;
if(indegree[p->adjno] == 0)
stack[++top] = p->adjno;
p = p->next;
}
}
if(cnt < AOV->vexnum)
{
std::cout << "TopSort failed" << std::endl;
delete AOV;
return -1;
}
std::cout << "TopSort success" << std::endl;
delete AOV; //排序完了之后删除,避免内存泄漏
return 0;
}
AdList * CreateVextexList(int Nodenum)
{
AdList *AOV = new AdList;
for(int i=0; i<=Nodenum ;i++) //使用一个专门的初始化函数更好
{
// AOV->vexs[i].first = new EdgeNode;
AOV->vexs[i].first = NULL;
}
VertexType elem;
int m;
int n;
int edge = 0;
AOV ->vexnum = Nodenum ; //顶点个数
while(Nodenum --)
{
std::cin >>elem >> m >> n; //表示m要在n之前完成
edge ++;
if( (m+n) != 0)
AOV->vexs[m].data = elem ;
/*
特别注意下面这个p使用的是双重指针,需要解释一下,
这个first为指向另一个结构体的指针,保存的是指针
的地址,将first赋给p,p的值和first相同,这是个
复制过程也就是说p是first的一个副本,所以当后面
p = q执行时,q的值又复制给q,此时p的值虽然改变
了,但是first值确不会变,所以需要一个指向first
的值的指针,即指向指针的指针,这时赋值后的值就
是直接改变p指向的值
*/
EdgeNode **p = &AOV->vexs[m].first;
EdgeNode *q = new EdgeNode;
q->adjno = n; //表示m指向n
q->next = NULL;
if( *p == NULL) //头插法
*p = q ;
// pp = qq; //指向指针的指针赋值指针的地址
else
{
q->next = (*p)->next; //每次查到first的后面
(*p)->next = q;
}
}
AOV ->arcnum = edge ; //边的条数
return AOV;
}
int main()
{
AdList *AOV = NULL;
int Nodenum;
std::cin >>Nodenum ;
AOV = CreateVextexList(Nodenum);
TopSort(AOV);
//GetInDegree(AOV);
}
没有全部验证,如果出现错误,请留言指正