拓扑排序 板子

转自:博客

拓扑排序

1.什么是拓扑排序
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图
中任 意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序
(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操
作称之为拓扑排序。 无向图和有环的有向图没有拓扑排序拓扑排序其实就是离散上的偏序关系的一个应用
2、拓扑排序的步骤:
1.按照一定的顺序进行构造有向图,记录后个节点的入度; 2,从图中选择一个入度为0的顶点,输出该顶点; 3,从图中
删除该顶点及所有与该顶点相连的边 4,重复上述两步,直至所有顶点输出。 5或者当前图中不存在入度为0的顶点为
止。此时可说明图中有环。 6,因此,也可以通过拓扑排序来判断一个图是否有环。

#include <iostream>
#include <vector>
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
int in[10010];
vector<int> v[10010];
int main()
{
    int n,m;
    while(~scanf("%d %d",&n,&m)&&n&&m)
    {
        memset(in,0,sizeof in);//清空入度
        for(int i=0;i<=n;i++) v[i].clear();
        for(int i=0;i<m;i++)
        {
            int x,y;
            cin >>x>>y;//比如x赢了y  或者y是x的儿子 ;那么就让x指向y;
            v[x].push_back(y);
            in[y]++;//y的入度加1
        }
        priority_queue<int,vector<int>,greater<int> >q;//优先队列,设置从小到大排序,小的在队列下面
        for(int i=0;i<n;i++)
        {
            if(in[i]==0)
                q.push(i);//把入度为0的节点压入队列
        }
        while(!q.empty())
        {
            int xx=q.top();
            q.pop();
            n--;//每次去掉一个节点
            for(int i=0;i<v[xx].size();i++)
            {
                int yy=v[xx][i];
                in[yy]--;
                if(!in[yy])
                    q.push(yy);//如果去掉上一个节点之后下一个节点的入度变为0,则压入队列中
            }
        }
        if(n) cout <<"NO"<<endl;//如果有环的话节点数不会为0
        else cout <<"YES"<<endl;
    }
    return 0;
}

例题

找到最终的安全状态

对于一个节点 u,如果我们从 u 开始任意行走能够走到一个环里,那么 u 就不是一个安全的节点。换句话说,u 是一个安全的节点,当且仅当 u 直接相连的节点(u 的出边相连的那些节点)都是安全的节点。

因此我们可以首先考虑没有任何出边的节点,它们一定都是安全的。随后我们再考虑仅与这些节点直接相连的节点,它们也一定是安全的,以此类推。这样我们可以将所有的边全部反向,首先所有没有任何入边的节点都是安全的,我们把这些节点全部移除。随后新的图中没有任何入边的节点都是安全的,以此类推。我们发现这种做法实际上就是对图进行拓扑排序。

算法

我们将所有的边反向,得到反向图 rgraph,随后将 rgraph 中所有没有入边的节点加入队列中。每一次我们取出队列中的一个节点 u,将它从图中删除,如果此时某个节点 v 存在从 u 到 v 的一条边,并且在删掉了这条边后,v 变成了没有入边的节点,那么就把 v 加入队列。以此类推,直到队列为空。最后所有加入过队列的节点即为安全的节点。

这个题,通过反向建立边来通过拓扑排序寻找到最终安全的结点,并在迭代中更新图。

代码:

class Solution {
public:
    vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
        int N = graph.size();
        vector<vector<int> > mp(N);
        queue<int> q;
        for (int i = 0;i!=N;i++) {
            if (graph[i].size()==0) q.push(i);
            for (int j = 0;j!=graph[i].size();j++) {
                mp[graph[i][j]].push_back(i);//反向建图
            }
        }
        vector<int> res;
        while(!q.empty()) {
            int t = q.front();
            q.pop();
            res.push_back(t);
            for (int i = 0;i!=mp[t].size();i++) {
                graph[mp[t][i]].erase(find(graph[mp[t][i]].begin(),graph[mp[t][i]].end(),t));//更新图
                if (graph[mp[t][i]].size()==0) q.push(mp[t][i]);
            }
            mp[t].clear();//可以不用更新
        }
        sort(res.begin(),res.end());
        return res;
    }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值