拓扑排序

Posted lif323

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了拓扑排序相关的知识,希望对你有一定的参考价值。

目录

定义

拓扑排序是图G的所有节点的一种线性次序,该次序满足下列条件:如果图G包含边(u,v),
则节点u在拓扑排序中处于节点v的前面(如果图G中包含环路,则不可能排出一个线性次序)。
也可以说,u,v之间存在一条可达路径,则u的拓扑序在v的前面。

算法实现

DFS

发现时间 d:该节点第一次被发现。
完成时间 f:该节点所有的可达节点都被发现。
使用DFS对有向无环图进行深度优先搜索,如果u.f < v.f,则在拓扑排序中u在v之前。
严谨的证明可以参考算法导论,不严谨的证明如下:如果v.f > u.f 则可以证明,图中存在一条v->u的可达路径,因为只有将v节点的所有可达节点都完成后,才会标记v的完成时间,
该时间一定比u的标记时间晚,也即是v.f  > u.f;参考上面关于拓扑排序 的定义,(完成时间)--> (u,v 之间存在路径)-->(u,v之间的拓扑序)可以知道,通过完成时间,我们可以确定
拓扑顺序,即完成时间越晚,拓扑序越靠前。

实现

代码中,当一个点的所有节点被发现,也即是该节点已完成,则根据完成时间与拓扑序关系,将其放入拓扑排序的数组里。
使用vector建立邻接表。
vector<int> nod[101];
int n;
int vis[101];//存放访问的时间
int order[101];//存放拓扑排序的顺序
int t = 0;
int num;
void dfs(int s)
{
    stack<int> st;
    st.push(s);
    vis[s] = ++t; 
    while(!st.empty())
    {
        int now = st.top();
        int len = nod[now].size();
        int id;
        bool has = false;
        for(int i = 0; i < len; ++i)
        {
            id = nod[now][i];
            if(vis[id] == 0)
            {
                st.push(id);
                vis[id] = ++t;
                has = true;
                break;
            }
        }
        if(!has)
        {
            st.pop();
            order[num--] = now;
        }
        
    }
 
} 

kahn算法

集合S保存所有入度为零的点
1.如果集合S为空,算法结束,否则:从所有节点中选出入度为零的点加入集合S.(可根据需要对集合S进行排序)
2.从集合S中取出入度为零的点u,放入已确定拓扑序的节点序列末尾。遍历点u 的所有相邻边,对可达节点入度减一。如果可达节点入度为零,则加入集合S.
3.回到1.
算法结束后,如果此时检测所有点的的入度,存在入度不为零的点,则说明图中存在环路。

可以理解为,如果入度为零,根据拓扑排序的定义。则不存在拓扑序更靠前的点,则该节点的拓扑序可以确定。

实现

使用vector存放邻接表。
vector<int> nod[27];
int n,m;
int order[27];
int in[27];
int t = 0;
int num;
int topoSort(){
    int ret = 1;
    memset(vis,0,sizeof(vis));
    memset(order,0,sizeof(order));
    num = 0;
    queue<int> q;
    for(int i = 1; i <= n; ++i){
        if(in[i] == 0)
        {
            q.push(i);
        }
    }
    while(!q.empty()){
        int now =  q.front();
        q.pop();
        int len = nod[now].size();
        for(int i = 0; i < len; ++i)
        {
            --in[nod[now][i]];
            if(in[nod[now][i]] == 0)
            {
                q.push(nod[now][i]);
            }
        }
        order[++num] = now;
    }
    for(int i = 1; i <= n; ++i)//存在入度不为零的点,存在环路,则拓扑排序不存在 。
    {
        if(in[i] != 0)
        {
            ret = -1;
        }
    }
    return ret;
}

例题
POJ2367 Genealogical tree
POJ1094 Sorting It All Out



以上是关于拓扑排序的主要内容,如果未能解决你的问题,请参考以下文章

拓扑排序代码:

使用 C# 代码实现拓扑排序

UVA10305 拓扑排序

【数据结构】请写出以下AOV网的拓扑排序序列

数据结构问题~啥图可以进行拓扑排序~啥图不能进行拓扑排序?

拓扑排序之变量序列代码