算法7-12:有向无环图的拓扑排序

题目描述

由某个集合上的一个偏序得到该集合上的一个全序,这个操作被称为拓扑排序。偏序和全序的定义分别如下:
若集合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 

提示

在本题中,需要严格的按照题目描述中的算法进行拓扑排序,并在排序的过程中将顶点依次储存下来,直到最终能够判定有向图中不包含回路之后,才能够进行输出。
另外,为了避免重复检测入度为零的顶点,可以通过一个栈结构维护当前处理过程中入度为零的顶点。

猜你喜欢

转载自blog.csdn.net/qq_63306482/article/details/124867777
今日推荐