拓扑排序

拓扑排序

   对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。

预备知识

一个较大的工程往往被划分成许多子工程,我们把这些子工程称作活动(activity)。在整个工程中,有些子工程(活动)必须在其它有关子工程完成之后才能开始,也就是说,一个子工程的开始是以它的所有前序子工程的结束为先决条件的,但有些子工程没有先决条件,可以安排在任何时间开始。为了形象地反映出整个工程中各个子工程(活动)之间的先后关系,可用一个有向图来表示,图中的顶点代表活动(子工程),图中的有向边代表活动的先后关系,即有向边的起点的活动是终点活动的前序活动,只有当起点活动完成之后,其终点活动才能进行。通常,我们把这种顶点表示活动、边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简称AOV网。

 

一个AOV网应该是一个有向无环图,即不应该带有回路,因为若带有回路,则回路上的所有活动都无法进行。如图3-6是一个具有三个顶点的回路,由<A,B>边可得B活动必须在A活动之后,由<B,C>边可得C活动必须在B活动之后,所以推出C活动必然在A活动之后,但由<C,A>边可得C活动必须在A活动之前,从而出现矛盾,使每一项活动都无法进行。这种情况若在程序中出现,则称为死锁或死循环,是应该必须避免的。

在AOV网中,若不存在回路,则所有活动可排列成一个线性序列,使得每个活动的所有前驱活动都排在该活动的前面,我们把此序列叫做拓扑序列(Topological order),由AOV网构造拓扑序列的过程叫做拓扑排序(Topological sort)。AOV网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称作它的拓扑序列。

由AOV网构造出拓扑序列的实际意义是:如果按照拓扑序列中的顶点次序,在开始每一项活动时,能够保证它的所有前驱活动都已完成,从而使整个工程顺序进行,不会出现冲突的情况。

执行步骤

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

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

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

循环结束后,若输出的顶点数小于网中的顶点数,则输出“有回路”信息,否则输出的顶点序列就是一种拓扑序列

在计算机语言中的应用

拓扑序列 C++(STL)核心代码

queue<int>q;

//priority_queue<int,vector<int>,greater<int>>q;

//优先队列的话,会按照数值大小有顺序的输出

//此处为了理解,暂时就用简单队列
inttopo()
{
    for(inti=1; i<=n; i++)   
    {
        if(indegree[i]==0)
        {
            q.push(i);      
        }
    }
    int temp;
    while(!q.empty())    
    {
        temp=q.front();//如果是优先队列,这里可以是top()
        printf("%d->",temp);
        q.pop();
        for(inti=1; i<=n; i++) //遍历从temp出发的每一条边,入度--
        {
            if(map[temp][i])
            {
                indegree[i]--;
                if(indegree[i]==0)q.push(i);
            }        
        }    
    }
}

c语言实现

void topsort()  
{  
    int i,j,t;  
    cnt=0;  
    for(t=1; t<=n; t++)//每次找一个点  
    {  
        for(i=1; i<=n; i++)  
            if(!in[i])  
            {  
                in[i]--;//防止下一次还搜到它  
                ans[cnt]=i;  
                cnt++;  
                for(j=1; j<=n; j++)  
                    if(map[i][j])  
                    {  
                        in[j]--;  
                    }  
                break;//控制从小到大输出  
            }  
        if(i>n)  
        {  
            printf("存在环"); //判断 是否有环    
            return ;  
        }  
    }  
}

拓扑排序基础(hdu1285)

#include<stdio.h>  
#include<string.h>  
int in[1000],match[1000][1000];//in[]存储点的入度 ,match[]存储图的临界阵   
int main()  
{  
    int m,n,i,j,p,q,k;  
    while(scanf("%d%d",&n,&m)!=EOF)  
    {  
        memset(match,0,sizeof(match));  
        memset(in,0,sizeof(in));  
        for(i=1;i<=m;i++)  
        {  
            scanf("%d%d",&p,&q);  
            if(match[p][q]==0)//此处别忘了重边的判断,否则会wa   
            {  
                match[p][q]=1;  
                in[q]++;//重边的时候,如果不做处理,度数为计算错误  
            }  
        }  
        //拓扑排序   
        for(i=1;i<=n;i++)//进行n次遍历,每次找出一个入度为0的节点                
            for(j=1;j<=n;j++)//遍历所有节点
                if(in[j]==0)//找出入度为0的节点   
                {  
                    in[j]--; //度数递减,避免下次继续找到   
                    if(i!=n)
                        printf("%d ",j);
                    else 
                        printf("%d\n",j);  
                    for(k=1;k<=n;k++)  
                    {  
                        if(match[j][k]==1)// 删除与度数为0的节点相关联的边   
                        in[k]--;  
                    }
                    break;  
                 }  
    }  
   return 0;  
}  

代码引用自https://blog.csdn.net/lieyingzhiyan/article/details/44035357

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值