关闭

拓扑排序

204人阅读 评论(0) 收藏 举报
分类:

拓扑排序

此代码来自于我的队友kls ,原博客:http://dlkkill.top/2017/06/15/%E6%9C%89%E5%85%B3%E6%8B%93%E6%89%91%E6%8E%92%E5%BA%8F/#comment-6

拓扑排序,是一种按照一定的先后规则,来进行排序。
一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序(来自百度百科)
简单来说,拓扑排序的规则,就像一棵技能树,要想点出某个技能,就要首先点出改技能的前置技能,然后才能将改技能点出。又或者想大家熟悉的大学选课系统一样,你必须先学过前导课程,才能更深入的学习其他过程。

排序实现思想
首先,我们可以将某种先后关系抽象成一种有向图的关系(如a在b前面,可以构建出一条有向边,由a指向b),而且必须保证该图不存在环路(否则会出现矛盾)。当构建出图以后,可以根据边的关系给点附带度数(对无向图来说,连着n条边的节点有n个度,就是顶点连着一条线段就代表一个度,这里仅对边的终点增加度数)。
图和度数建立完成后,就要开始不断搜索度数为零的点,输出该点并减少该点指向点的度数。不断循环,直至输出所有点拓扑排序完成为止。

排序代码实现

1.暴力寻求

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#define MAX 100
usingnamespace std;

void toposort(int map[MAX][MAX],int indegree[MAX],int n)
{
    int i,j,k;
    for(i=0;i<n;i++) //遍历n次
    {
        for(j=0;j<n;j++) //找出入度为0的节点
        {
            if(indegree[j]==0)
            {
                indegree[j]--;
                cout<<j<<endl;
                for(k=0;k<n;k++) //删除与该节点关联的边
                {
                    if(map[j][k]==1)
                    {
                        indegree[k]--;
                    }
                }
                break;
            }
        }
    }
}


int main(void)
{
    int n,m; //n:关联的边数,m:节点数
    while(scanf("%d %d",&n,&m)==2&&n!=0)
    {
        int i;
        int x,y;
        int map[MAX][MAX]; //邻接矩阵
        int indegree[MAX]; //入度
        memset(map,0,sizeof(map));
        memset(indegree,0,sizeof(indegree));
        for(i=0;i<n;i++)
        {
            scanf("%d %d",&x,&y);
            if(!map[x][y])
            {
                map[x][y]=1;
                indegree[y]++;
            }
        }
        toposort(map,indegree,m);
    }
    return0;
}

该代码来自于博客:
**dlkkill.top
**
这份代码的主要思想就是暴力求解,弱有n个顶点,就暴力搜索n次,每次找到一个度数为零的点,然后暴力搜索该点有关的边,继续暴力。
这份代码写起来简单方便,但是时间会比较慢。

2.(队列+ 邻接矩阵)优化拓扑排序

与前一份代码类似,不过运用了数据结构进行优化。
但是代码长度较前一份长,编写时间也会较长。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
vector<int> nodes[1010];
int degress[1010];
int n;
queue<int> dequeue;
int cnt=0;
int num[1010];
void toposort()
{
    while(!dequeue.empty())
    {
        int t=dequeue.front();
        dequeue.pop();
        num[cnt++]=t;
        for(int i=0; i<nodes[t].size(); i++)
        {
            int h=nodes[t][i];
            degress[h]--;
            if(degress[h]==0)
                dequeue.push(h);
        }
    }
}
int main()
{
    //int n;
    cin>>n;
    int a,b;
    for(int i=1; i<=n; i++)
    {
        int t;
        while(scanf("%d",&t)&&t)
        {
            degress[t]++;
            nodes[i].push_back(t);
        }
    }
    for(int i=1; i<=n; i++)
        if(!degress[i])
            dequeue.push(i);
    toposort();
    for(int i=0; i<n; i++)
    {
        if(i!=0)
            cout<<" ";
        cout<<num[i];
    }
    cout<<endl;
    return 0;
}

此代码用于拓扑排序入门题目:poj2367

3.dfs回溯实现拓扑排序

核心代码:

vector<int>g[N];//邻接表存储  
int vis[N],topo[N],cnt;  

bool dfs(int u)  
{  
    vis[u] = -1;//-1用来表示顶点u正在访问  
    for(int i = 0 ; i < g[u].size() ; i ++)  
    {  
        if(vis[g[u][i]] == -1)//表示这个点进入了两次,肯定出现了环  
            return false;  
        else if(vis[g[u][i]] == 0)  
        {  
            dfs(g[u][i]);  
        }  
    }  
    vis[u] = 1;  
    topo[cnt++] = u;//放到结果数组里,输出的时候记得倒序输出,(回溯的原因)  
    return true;  
}  

bool toposort(int n)  
{  
    memset(vis,0,sizeof(vis));  
    for(int i = 1 ; i <= n ; i ++)  
    {  
        if(!vis[i])  
        {  
            if(!dfs(i)) return false;//huan  
        }  
    }  
    return true;  
}  

具体详见博文:http://blog.csdn.net/acceptedxukai/article/details/6959882

1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:8903次
    • 积分:761
    • 等级:
    • 排名:千里之外
    • 原创:68篇
    • 转载:2篇
    • 译文:0篇
    • 评论:3条