拓扑排序

拓扑排序

什么是拓扑排序?
一个较大的工程往往被划分成许多子工程,我们把这些子工程称作活动(activity)。在整个工程中,有些子工程(活动)必须在其它有关子工程完成之后才能开始,也就是说,一个子工程的开始是以它的所有前序子工程的结束为先决条件的,但有些子工程没有先决条件可以安排在任何时间开始。

注意事项
这也就产生了优先次序,我们需要把没有前提条件的时间最先处理,有条件的事件需要在条件事件完成以后再进行处理。

分析
因为没有前提条件的事件可能不止一个,所以就决定了我们的起始位置不唯一,也就是拓扑排序的结果也不唯一。
其次如果所有的事件都有前提条件,我们将无法找到起始位置或者中间某一过程无法找到下一个事件的位置,这时我们应该考虑结构中出现了回路。
因此我们要对输出数据的个数进行统计,以方便我们对后来的输出进行判断,判断是否所有时间排序完毕。

拓扑排序思想
由AOV网构造拓扑序列的拓扑排序算法主要是循环执行以下两步,直到不存在入度为0的顶点为止。
(1) 选择一个入度为0的顶点并输出之;
(2) 从网中删除此顶点及所有出边。
循环结束后,若输出的顶点数小于网中的顶点数,则输出“有回路”信息,否则输出的顶点序列就是一种拓扑序列。
拓扑例子
拓扑排序实现
首先我们需要建立邻接矩阵,什么是邻接矩阵呢?我们需要将各个事件之间的关系统统表示出来,建立一个二维的邻接矩阵用来表示各个位置的对应关系,两个事件之间没有关系用0表示,有关系用1表示:
memset只能初始化为0或-1

    int map[13][13];//定义一个大小足够用的矩阵
    memset(map,0,sizeof(map));//邻接矩阵的初始化<memory><string.h>
    for(int i=1;i<=12;i++)//输出初始化的邻接矩阵
    {
        for(int j=1;j<=12;j++)
        {
            cout<<map[i][j];
        }cout<<endl;
    }

像上图中的例子一样,我们可以输入的a,b数据就是表示边的两头事件的对应关系
由图可以看出来输入样例应该是
顶点数12 边数14所以
Input
12 14 顶点和边
1 2 表示1和2存在关系
2 4 ~2和4存在关系
1 7 ~以此类推
1 3
5 4
4 6
3 5
7 8
7 9
9 8
8 10
10 6
6 11
11 12

int a,b;//用来输入边的两个顶点的数据
    int v,l;//顶点 边
    cout<<"输入顶点和边个数:"<<endl;
    cin>>v>>l;
    for(int i=1;i<=l;i++)
    {
        cin>>a>>b;
        map[a][b]=1;//输入边的两个顶点后 在对应坐标做标记表示存在对应关系
    }
    for(int i=1;i<=12;i++)//输出邻接矩阵
    {
        for(int j=1;j<=12;j++)
        {
            cout<<map[i][j];
        }cout<<endl;
    }

我们得到的会是一个邻接矩阵
我们自己给他加上二位数组的位置
可以看出来第一行第二个为1,表示1->2
第一行第三个为1,表示1->3
以此类推
这就是这个邻接矩阵的含义,下面我们来思考如何使用邻接矩阵来进行拓扑排序
邻接矩阵
邻接矩阵建立完毕以后,我们需要对各个事件的前提事件个数进行统计,也就是各个点的入度个数。

int rd[13]={0};//初始化入度个数
    for(int i=1;i<=12;i++)
    {
        for(int j=1;j<=12;j++)
        {                   //map[j][i]==1表示两事件之间存在关系,入度+1
            if(map[j][i]==1)//注意j,i的位置,这里是i对j的关系,所以是j的入度,想象二维矩阵我们保持i不变,改变j,得到的是i的入度
                rd[i]++;
        }
    }

邻接矩阵我们竖着看表示每个事件的入度个数,思考一下为什么,很简单,->被指向的事件。
我们通过对一列一列的数据进行遍历,能够得到每个事件的入度个数,然后根据拓扑排序的算法思想进行实现,让入度为零的事件优先输出,然后使它相邻的(也就是作为别的事件的前提)事件的前提就会减少这个事件的条件,也就是与他有关系(在它之后才能进行的事件)的入度(条件)就会-1,就模拟我们把这个事件给删除掉。
准备我们已经做完了,最后我们来进行拓扑排序:

cout<<"拓扑排序:"<<endl;
    int count=0;//计数器,记录输出个数
    while(1)
    {
        for(int i=1;i<=12;i++)
        {
            if(rd[i]==0){//从存有入度的数组中查找入度为零的事件i,入度为0就可以优先安排直接输出即可
                cout<<i<<" ";
                count++;//记录下来已经输出的事件个数
                rd[i]=-1;//表示已经输出过该事件
                for(int j=1;j<=12;j++)
                {
                    if(map[i][j]==1)//判断所有与该事件有关的事件
                        rd[j]--;//此时i已经做完,前提条件里有事件i的事件的条件都会-1
                }
                }
        }
        if(count==12)break;//这样写只能是满足条件的拓扑排序可以正常输出,所以我们可以稍作改进
    }

最后我们来看一下代码运行的结果:
运行结果
本文参考学习:https://blog.csdn.net/qq_37618797/article/details/81070577 得到了一些自己的感悟和体会,对作者表示感谢。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值