题目描述
由某个集合上的一个偏序得到该集合上的一个全序,这个操作被称为拓扑排序。偏序和全序的定义分别如下:
若集合X上的关系R是自反的、反对称的和传递的,则称R是集合X上的偏序关系。
设R是集合X上的偏序,如果对每个x,y∈X必有xRy或yRx,则称R是集合X上的全序关系。
由偏序定义得到拓扑有序的操作便是拓扑排序。
拓扑排序的流程如下:
1. 在有向图中选一个没有前驱的顶点并且输出之;
2. 从图中删除该顶点和所有以它为尾的弧。
重复上述两步,直至全部顶点均已输出,或者当前图中不存在无前驱的顶点为止。后一种情况则说明有向图中存在环。
采用邻接表存储有向图,并通过栈来暂存所有入度为零的顶点,可以描述拓扑排序的算法如下:
在本题中,读入一个有向图的邻接矩阵(即数组表示),建立有向图并按照以上描述中的算法判断此图是否有回路,如果没有回路则输出拓扑有序的顶点序列。
输入
输入的第一行包含一个正整数n,表示图中共有n个顶点。其中n不超过50。
以后的n行中每行有n个用空格隔开的整数0或1,对于第i行的第j个整数,如果为1,则表示第i个顶点有指向第j个顶点的有向边,0表示没有i指向j的有向边。当i和j相等的时候,保证对应的整数为0。
输出
如果读入的有向图含有回路,请输出“ERROR”,不包括引号。
如果读入的有向图不含有回路,请按照题目描述中的算法依次输出图的拓扑有序序列,每个整数后输出一个空格。
请注意行尾输出换行。
#include <iostream>
#include <iomanip>
using namespace std;
#define Max 1000
#define MVNum 100 //最大顶点数
#define OK 1
#define ERROR 0
#define OVERFLOW - 2
#define MAXSIZE 10 //顺序栈存储空间的初始分配量
typedef int Status;
typedef int SElemType;
typedef int VerTexType; //顶点信息
typedef int OtherInfo; //和边相关的信息
typedef int ArcType;
//- - - - -图的邻接表存储表示- - - - -
typedef struct {
VerTexType vexs[MVNum]; //顶点表
ArcType arcs[MVNum][MVNum]; //邻接矩阵
int vexnum, arcnum; //图的当前点数和边数
} Graph;
typedef struct {
SElemType *base;//栈底指针
SElemType *top;//栈顶指针
int stacksize;//栈可用的最大容量
} SqStack;
//算法3.1 顺序栈的初始化
Status InitStack(SqStack &s)
{
//构造一个空栈S
s.base = new SElemType[MAXSIZE];//为顺序栈动态分配一个最大容量为MAXSIZE的数组空间
if(!s.base)
exit(OVERFLOW); //存储分配失败
s.top = s.base; //top初始为base,空栈
s.stacksize = MAXSIZE; //stacksize置为栈的最大容量MAXSIZE
return OK;
}
Status Push(SqStack &s, SElemType e)
{
/****在此下面完成代码***************/
if(s.top-s.base==s.stacksize)return ERROR;
*s.top=e;
s.top++;
return OK;
/***********************************/
}
//算法3.3 顺序栈的出栈
Status Pop(SqStack &s, SElemType &e)
{
/****在此下面完成代码***************/
if(s.base==s.top)return ERROR;
s.top--;
e=*s.top;
return OK;
/***********************************/
}
bool StackEmpty(SqStack S)
{
/****在此下面完成代码***************/
if(S.top==S.base)return true;
else return false;
/***********************************/
}
void CreateUDG(Graph &g)
{
//采用邻接矩阵表示法,创建无向图G
/****在此下面完成代码***************/
int i,j;
cin>>g.vexnum;
for(i=0;i<g.vexnum;i++)
{
for(j=0;j<g.vexnum;j++)
{
cin>>g.arcs[i][j];
}
}
/***********************************/
}//CreateUDN
Status TopSort(Graph g,int topo[])
{
int i,j,k,m=0,indegree[g.vexnum];
SqStack s;
InitStack(s);
for(i=0;i<g.vexnum;i++)
indegree[i]=0;
for(i=0;i<g.vexnum;i++)
{
for(j=0;j<g.vexnum;j++)
{
indegree[i]=indegree[i]+g.arcs[j][i];
}
}
for(i=0;i<g.vexnum;i++)
{
if(!indegree[i])
{
g.arcs[i][i]=1;
Push(s,i);
}
}
while(!StackEmpty(s))
{
Pop(s,i);
topo[m++]=i;
for(j=0;j<g.vexnum;j++)
{
if(g.arcs[i][j]!=0&&i!=j)
{
g.arcs[i][j]=0;indegree[j]--;
}
if(g.arcs[j][j]==0&&indegree[j]==0)
{
g.arcs[j][j]=1;
Push(s,j);
}
}
}
if(m<g.vexnum)
{
cout<<"ERROR";return 0;
}else
{
for(i=0;i<g.vexnum;i++)
{
cout<<topo[i]<<" ";
}
return 1;
}
}
int main()
{
Graph g;
CreateUDG(g);
int topo[g.vexnum];
TopSort(g,topo);
return 0;
}//main
样例输入 Copy
4 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0
样例输出 Copy
3 0 1 2
提示
在本题中,需要严格的按照题目描述中的算法进行拓扑排序,并在排序的过程中将顶点依次储存下来,直到最终能够判定有向图中不包含回路之后,才能够进行输出。
另外,为了避免重复检测入度为零的顶点,可以通过一个栈结构维护当前处理过程中入度为零的顶点。