拓扑排序

1、 拓扑排序解释

  我们起床穿裤子和鞋子时,相信大部分人的顺序是这样的,先穿上内裤,然后再穿上裤子,再穿上袜子,然后才是鞋子。  那么,我们把这些步骤分解:

(1)穿内裤

(2)穿裤子

(3)穿袜子

(4)穿鞋子

我们把这四个步骤,按照上述的顺序 给排一下,这就是所谓的拓扑排序。

当然这个排序的顺序是唯一的,如果你先进行(2)然后(1)(3)(4),哦,不,你不是超人,请不要这样做, 又假如你按照(1)(2)(4)(3), 那显然也是不行的。

拓扑排序 也可以描述一个暑假写作业的过程 : 语文作业,数学作业,英语作业,生物作业,化学作业,物理作业。

(1)  语文

(2)  数学

(3)  英语

(4)  生物

(5)  化学

(6)  物理

你可以是(1)(2)(3)(4)(5)(6),也可以是(6)(5)(4)(3)(2)(1),再者英语老师比较凶,那么可以是(3)(1)(2)(4)(5)(6)。等等其他的排序方式。

那么这个排序又是不唯一的。

因此  拓扑排序可能是唯一的又有可能是不唯一的。

就像 3个篮球队进行比赛。  编号分别为 1  , 2 , 3。

1打赢了2

2打赢了3

3打赢了1。 问谁是最后的冠军。 各一胜一负你问我谁是冠军 ,这不是扯蛋嘛。 So,这是不能判断谁是冠军的,  因为这个事件存在一个 环,互相牵制,进行排序是不行产生结果的。

如果这样  :

1打赢了2

3打赢了2

那么最后的冠军可能是不确定的,因为你不知道1和3 谁强。 所以只能是 1,3并列了,你如果喜欢大数在前 那就是3 1 2,反之,就是1 3 2了。

拓扑排序其实就是这个样子。

前面大篇幅的扯犊子,主要是介绍什么是拓扑排序。 那么我们要讨论一下,怎么样进行拓扑排序呢?  哎,这个问题好!

插播 :

我们再次的从 1  2  3  这三支队伍的冠军争夺赛说起。

1打赢了2  因为2输了一场比赛,所以要给2做一标记。因此2号的菊花上就出现了一杆长枪。 我们称这个标记为 入度 那么2的入度就是 1了。

3打赢了2   因为2又输了一场比赛,又是一杆长枪啊。为什么受伤的总是2。  那么2的入度 就++了  变成了2。

好了  这就是 什么是  入度  了。  如果你还不是很懂入度是什么。那我告诉你,入度 在这里就是2号被打败了几次。

那我们 就要 进入正题了。

拓扑排序 :

  由AOV网构造拓扑序列的拓扑排序算法主要是循环执行以下两步,直到不存在入度为0的顶点为止。

  (1) 选择一个入度为0的顶点并输出之;

  (2) 从网中删除此顶点及所有出边。

  循环结束后,若输出的顶点数小于网中的顶点数,则输出“有回路”信息,否则输出的顶点序列就是一种拓扑序列。 (摘自 : 百度百科)

我们拿例题来讲拓扑排序:题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1285

因为这个题目相对来说比较简单,所以可以用普通的数组储存来说明

题解一号:

#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<set>
using namespace std;
int n,m;
int from[505][505];
int indrgee[505];
int vis[505];
int topsort()
{
    int cnt=0;
    while(cnt!=n)
    {
        for(int i=1;i<=n;i++)//便利入度数目为零的输出
        {
            if(indrgee[i]==0&&!vis[i])
            {
                if(cnt>=1)
                    printf(" %d",i);
                else
                    printf("%d",i);
                for(int j=1;j<=n;j++)//把刚才输出的入度数目为零的点以及边全部删除
                {
                    if(from[i][j])
                        indrgee[j]--;
                }
                cnt++;
                vis[i]=1;
                break;
            }
        }
    }

    printf("\n");
}
int main()
{

    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(indrgee,0,sizeof(indrgee));
        memset(from,0,sizeof(from));
        memset(vis,0,sizeof(vis));
        for(int i=0;i<m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            if(!from[a][b])//如果已经加入了边,不必重复加入
            {
                from[a][b]=1;
                indrgee[b]++;//入度数目加一
            }
         
        }
        topsort();
    }


    return 0;
}

由于这个题解只能在复杂度低的情况下使用,所以说我们需要进一步更新:利用优先队列和链式前向星来优化,用链式前向星来储存

#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<set>
#include<queue>

using namespace std;
int n,m;
struct edge{
    int v,next;
}e[100005];
int head[100005];
int indrgee[100005];
int cnt;
void topsort()
{
    int sum=0;
   priority_queue<int, vector<int>, greater<int> >q;//优先队列从小到大排列
   for(int i=1;i<=n;i++)
   {
       if(indrgee[i]==0)
       {
           q.push(i);
           indrgee[i]--;
       }
   }
   while(!q.empty())
   {
       int no;
       no=q.top();
       q.pop();
       sum++;
       if(sum>1)
       {
            printf(" %d",no);
       }
        else
        printf("%d",no);
       for(int i=head[no];~i;i=e[i].next)
       {
           indrgee[e[i].v]--;
           if(indrgee[e[i].v]==0)
           {
                q.push(e[i].v);

           }
       }
   }
}
int main()
{

    while(scanf("%d%d",&n,&m)!=EOF)
    {
        cnt=0;
        memset(head,-1,sizeof(head));
        memset(indrgee,0,sizeof(indrgee));//记录各个点的入度数
        int i;
        for(int j=0;j<m;j++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            for( i=head[u];~i;i=e[i].next)//

                if(e[i].v==v)
                    break;

            if(i==-1)
            {
               indrgee[v]++;
               e[cnt].v=v;
               e[cnt].next=head[u];
               head[u]=cnt++;
            }

        }
        topsort();
        printf("\n");

    }


    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值