拓扑排序

拓扑排序,顾名思义,就是一种排序方法。这是一种什么排序?这种排序的作用?然后怎么去实现这种排序算法?现在就让我们仔细研究下。

1、什么是拓扑排序,也就是拓扑排序的概念

实际上,拓扑排序是一种图论算法,该算法在《数据结构与算法》一书中有涉猎。引用维基百科的定义:

在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序(英语:Topological sorting)。
(1)每个顶点出现且只出现一次;
(2)若A在序列中排在B的前面,则在图中不存在从B到A的路径。

也可以定义为:拓扑排序是对有向无环图的顶点的一种排序,它使得如果存在一条从顶点A到顶点B的路径,那么在排序中B出现在A的后面。

是不是觉得看完概念还是很晕的感觉,下面就用一个实例来讲具体的拓扑排序样例。


(a)有向图网(AOV)  (b)输出v6后       (c)输出v1后    (d)输出v4后 (e)输出v3后 (f)输出v2后

                                                           输出排序结果:v6-v1-v4-v3-v2-v5

此拓扑排序的思想是:

(1)从有向图中选取一个没有前驱的顶点,并输出之;

(2)从有向图中删去此顶点以及所有以它为尾的弧;

重复上述两步,直至图空,或者图不空但找不到无前驱的顶点为止。没有前驱 -- 入度为零,删除顶点及以它为尾的弧-- 弧头顶点的入度减1。

何谓入度?

我觉得得先明白什么是度?度(Degree):一个顶点的度是指与该顶点相关联的边的条数,顶点v的度记作d(v)。

入度:对于有向图来说,一个顶点的度可细分为入度和出度。一个顶点的入度是指与其关联的各边之中,以其为终点的边数。

出度:出度则是相对的概念,指以该顶点为起点的边数。

以v6这个顶点为例,它的入度为0,出度为2。

以v5这个顶点为例,它的入度为3,出度为0。

以v4这个顶点为例,它的入度为2,出度为1。

以v3这个顶点为例,它的入度为1,出度为2。

以v2这个顶点为例,它的入度为2,出度为0。

以v1这个顶点为例,它的入度为0,出度为3。

经验证,一个有向五环图中所有顶点的入度之和(0+3+2+1+2+0=8)等于所有顶点的出度之和(2+0+1+2+0+3=8)。

2、拓扑排序的作用

不禁有人就问了,有很多排序算法啊,快速排序,插值排序,这个排序到底有什么优点呢?平常这种排序又用于哪种场景呢?

我们说快速排序是不稳定的,这是因为最后的快排结果中相同元素的出现顺序和排序前不一致了。如果用偏序的概念可以这样解释这一现象:相同值的元素之间的关系是无法确定的。因此它们在最终的结果中的出现顺序可以是任意的。而对于诸如插入排序这种稳定性排序,它们对于值相同的元素,还有一个潜在的比较方式,即比较它们的出现顺序,出现靠前的元素大于出现后出现的元素。因此通过这一潜在的比较,将偏序关系转换为了全序关系,从而保证了结果的唯一性。而拓扑排序就是一种将偏序转换为全序的一种算法。

这里要补充两个概念,偏序和全序?

偏序:有向图中两个顶点之间不存在环路,至于连通与否,是无所谓的。

全序:就是在偏序的基础之上,有向无环图中的任意一对顶点还需要有明确的关系(反映在图中,就是单向连通的关系,注意不能双向连通,那就成环了)。

意思就是讲,一个不确定的偏序关系经全序后就有一种确定的先后顺序了。

既然有先后,那么在实际生活中的选课问题,比如大一时一定要修完这门课,大二才学第二门课,这种排课问题就是拓扑排序问题。




对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。

        一个有向图无法拓扑排序时只有一种情况:该有向图中存在环。下面给出简单的判定有向图是否可拓扑排序的代码:

题意:给你一个N点和M条有向边的图,问你该图是否可拓扑排序.

分析:没什么可说的,直接用topo()函数判断所给的有向图是否可拓扑排序即可.

AC代码:


    #include<cstdio>  
    #include<cstring>  
    #include<vector>  
    #include<queue>  
    using namespace std;  
    const int maxn=100+10;  
    int n,m;  
    vector<int> G[maxn];//G[i]表示i节点所指向的所有其他点  
    int in[maxn];//节点入度  
    bool topo()//判断该图是否可拓扑排序  
    {  
        queue<int> Q;  
        int sum=0;//记录可拆解的点数目  
        for(int i=0;i<n;i++)if(in[i]==0)  
            Q.push(i);  
        while(!Q.empty())  
        {  
            int u=Q.front(); Q.pop();  
            sum++;  
            for(int i=0;i<G[u].size();i++)  
            {  
                int v=G[u][i];  
                if(--in[v]==0) Q.push(v);  
            }  
        }  
        return sum==n;//可完全拓扑  
    }  
    int main()  
    {  
        while(scanf("%d%d",&n,&m)==2&&n)  
        {  
            memset(in,0,sizeof(in));  
            for(int i=0;i<n;i++) G[i].clear();  
            for(int i=0;i<m;i++)  
            {  
                int u,v;  
                scanf("%d%d",&u,&v);  
                G[u].push_back(v);  
                in[v]++;  
            }  
            printf("%s\n",topo()?"YES":"NO");  
        }  
        return 0;  
    }  



拓扑排序应用

POJ 1270 Following Orders(拓扑排序:输出所有可能):按字典序回溯构造所有可能的拓扑序列。解题报告

POJ 2585 Window Pains(拓扑排序判定):将原问题转化为拓扑排序判定问题即可。解题报告

POJ 2367 Genealogical tree(拓扑排序:输出方案):输出一个可行的拓扑序列。解题报告!

POJ 1094 Sorting It All Out(拓扑排序):判断该拓扑排序是否是一个全序。解题报告

HDU 2647 Reward(拓扑排序):拓扑图分层。解题报告!

HDU 1285 确定比赛名次(简单拓扑排序):输出字典序最小的拓扑序列。解题报告

HDU 3342 Legal or Not(简单拓扑排序判定):简单拓扑判断。解题报告!

HDU 1811 Rank of Tetris(并查集+拓扑排序):如何处理间接的相等关系。解题报告

HDU 4324 Triangle LOVE(拓扑排序):拓扑判定,需要推导结论。解题报告!



点击打开链接

点击打开链接

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值