前言

在看到“拓扑序列”这4个字的时候,笔者人是傻的。拓扑序列是啥?听着就感觉好厉害!然后,当我得知“拓扑”两个字其实是一个大家都知道的单词“Top”的音译时,我不禁在想:翻译成这样,估计是故意让大家觉得这个知识点很难的吧。事实证明的确是这样,拓扑排序的概念与实现都是非常简单的。别被看上去高大上的名字吓到了。

拓扑序列概念介绍

首先,我们给出一个如图的有向图

然后我们给出一个序列a={1,2,3,4},我们随便在有向图中选择一条边,我们都能满足在序列a中,这条边起点的编号一定在终点的编号之前。那么,我们就称为序列a是这个有向图的拓扑序列。

根据拓扑序列的概念,我们能知道:对于某些有向图而言,它的拓扑序列是不唯一的。只要是一个序列满足拓扑序列的定义,我们就可以说这个序列是这张图的拓扑序列。

当然,并不是所有图都是存在拓扑序列的,如果我们发现有向图中存在环,那么无论如何我们也找不出一个序列满足拓扑序列的定义。

如图,我们发现如果在之前的图中加上一条边,那么这张图就没有拓扑序列了。

拓扑序列求法介绍

首先,我们需要复习(预习?)一下离散数学中的知识点,入度。
所谓入度,就是对于有向图中的一个点而言,有多少个箭头是指向它的的。比如在最上方的没有环的有向图中,点1的入度为0,点2与3的入度都为1,点3的入度为2。

在了解了入度后,我们开始进行拓扑排序,在排序中,我们使用的是BFS广度优先搜索的思路进行点的遍历。
0.初始化:建图,求出所有点的入度。
1.我们首先遍历全图要找到入度为0的点,我们在我们的图中首先找到了点1

广度遍历所有以1为起点的边,删除这些边,操作为点2的入度减去1,然后就是这样,我们把点1放进拓扑序列中的最前面,a={1};

再次循环遍历所有还没有放进序列的点,我们发现图中点2的入度已经变成0了,标记出来重复对点1的操作,a={1,2}

继续遍历,我们找到了点3,操作。a={1,2,3}

最后找到了点4,所有点都操作完毕,a={1,2,3,4},拓扑排序结束。

没有拓扑序列的有环图判断

之前我们说过,并不是所有的有向图都存在拓扑序列的,那么我们要怎么来进行判断呢?首先,我们放一个存在环的有向图来进行上述的算法

我们对于上述图按照之前的算法进行处理完点1,2后,会变成这样

此时,a={1,2},按照算法,我们应该在剩下的点中寻找入度为0的点,然后我们发现,找不到了,由于有环的存在,我们已经无法在接下来的图中找到入度为0的点了,算法结束,最后序列a定格在a={1,2}无法继续更新。

如果根据上面的算法判断,程序会认为此时的a就是最后的拓扑序列,但是显然不是。我们发现a中的点的数量为2,小于点的总数5。所以,假如我们求出最后的序列中点的数量少于点的总数,那么我们就知道有向图中存在环,该图不存在拓扑序列。

拓扑排序的代码实现

例题链接:Acwing 有向图的拓扑序列

给定一个 n 个点 m 条边的有向图,点的编号是 1 到 n,图中可能存在重边和自环。请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1。若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列。输入格式
第一行包含两个整数 n 和 m。接下来 m 行,每行包含两个整数 x 和 y,表示存在一条从点 x 到点 y 的有向边 (x,y)。输出格式
共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。
否则输出 −1。数据范围
1≤n,m≤1e5
输入样例:
3 3
1 2
2 3
1 3
输出样例:
1 2 3

这道题就是一道板子题,所以我们直接贴上代码,不过要看懂代码你需要事先知道以下知识点
1.c++STL中 数据结构queue的基本使用方法
2.链式前向星的存图方法----->链式前向星CSDN博客
3.作者重定义的memset函数( #define mem(a,b) memset(a,b,sizeof(a)) )

//#pragma GCC optimize(2)
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<ctime>
#include<cstring>
#include<list>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef  pair<int, int> PII;
const int N = 1e5 + 7;
int n, m;
int e[N], ne[N], h[N], id = 1;  //链式前向星存图
int rd[N];  //存放每个点的入度
bool ch[N];  //用来判断该点有没有被放进序列中
bool haveans = true;  //用来判断是否有拓扑序列
queue<int>q;  //用来存放拓扑序列的队列void add(int a, int b)   //加边的函数
{e[id] = b;ne[id] = h[a];h[a] = id++;rd[b]++;
}void topsort()   //拓扑排序实现函数
{bool flag = true;  //flag为true就代表还能找到入度为0的点while (flag){flag = false;  //先改为,找不到入度为0的点for (int i = 1; i <= n; i++){if (rd[i] == 0&&!ch[i]){ch[i] = true;   flag = true;   //找到了入度为0的点就把flag改为true,继续循环q.push(i);   //把入度为0的点放进队列qfor (int j = h[i]; j != -1; j = ne[j])    //BFS遍历所有以i为起点的边{int k = e[j];rd[k]--;  //每一条边的入度减一,删除该边}}}}}void solve()
{bool flag = true;cin >> n >> m;mem(h, -1);for (int i = 0; i < m; i++){int a, b;cin >> a >> b;add(a, b);}topsort();if (q.size() != n)   //如果队列中元素数量不等于点的总数就代表图中有环,没有拓扑序列cout << "-1" << endl;else   //否则输出拓扑序列while (!q.empty()){cout << q.front();q.pop();if (!q.empty())cout << ' ';elsecout << endl;}}int main()
{//std::ios::sync_with_stdio(false);//cin.tie(0), cout.tie(0);solve();return 0;
}

作者:Avalon Demerzel,喜欢我的博客就点个赞吧,更多图论与数据结构知识点请见作者专栏《图论与数据结构》

【图论】拓扑排序:一个名字高大上的实际很简单的算法(图文详解)相关推荐

  1. 七大排序算法—图文详解(插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序)

    作者:渴望力量的土狗 博客主页:渴望力量的土狗的博客主页 专栏:数据结构与算法 工欲善其事必先利其器,给大家介绍一款超牛的斩获大厂offer利器--牛客网 点击免费注册和我一起刷题吧 目录 插入排序: ...

  2. storyboard(故事版)新手教程 图文详解 1.创建一个无约束的导航栏加选项卡(tabbar)故事版

    OSX系统10.10.5 Xcode版本7.1 本文图文详解如何初步使用故事版进行开发 1.打开Xcode  点击Create a new Xcode project 选择Single View Ap ...

  3. 【数据结构与算法】详解什么是图结构,并用代码手动实现一个图结构

    本系列文章[数据结构与算法]所有完整代码已上传 github,想要完整代码的小伙伴可以直接去那获取,可以的话欢迎点个Star哦~下面放上跳转链接 https://github.com/Lpyexplo ...

  4. 实现一个域名对应多个IP地址和DNS优缺点详解!

    实现一个域名对应多个IP地址和DNS优缺点详解! 1.DNS定义: DNS(Domain Name System)是因特网的一项服务,它作为域名和IP地址相互映射的一个分布式数据库,能够使人更方便的访 ...

  5. JavaScript数据结构与算法——列表详解(下),基于Nodejs实现一个列表应用

    1.上篇回顾: 上篇我们实现了一个列表类,并添加了一些属性,实现了比较多的方法,本文章将与大家一起使用列表实现一个图书借阅查询系统.需要使用JavaScript数据结构与算法--列表详解(上)中写好的 ...

  6. java 一个大事务下的新增、修改、查询_一文带你轻松搞懂事务隔离级别(图文详解)...

    点击上方"linkoffer", 选择关注公众号高薪职位第一时间送达 本文由 SnailClimb 和读者 BugSpeak 共同完成. 事务隔离级别(图文详解) 什么是事务? 事 ...

  7. 图论--拓扑排序--判断一个图能否被拓扑排序

    拓扑排序的实现条件,以及结合应用场景,我们都能得到拓扑排序适用于DAG图(Directed Acyclic Graph简称DAG)有向无环图, 根据关系我们能得到一个线性序列,实现的方式是DFS,具体 ...

  8. 图论---拓扑排序的应用

    最近研究了几道图论的题目,都是图论入门的算法,用的比较多的就是拓扑排序,多用于有着先后顺序的题目,也可以用来判断环,做个小总结. 杂物 题目链接:杂务 - 洛谷 这一题需要计算最短的时间,利用了记忆化 ...

  9. 数据结构-图论-拓扑排序模板题(hdu3342)(poj1270)(hdu4857)

    dfs与bfs的很直接的应用就是拓扑排序. 拓扑排序如果用数组来模拟链表进行操作,既解决了稀疏图的空间问题,又解决了用链表进行操作麻烦的问题 但是拓扑排序并不是数字大小之间的排序,而是某些事情之间的顺 ...

  10. 图论--拓扑排序--HDU-1285确定比赛名次

    Problem Description 有N个比赛队(1<=N<=500),编号依次为1,2,3,....,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委 ...

最新文章

  1. hive2.1.1安装配置
  2. 2020百度云秀最新成绩单,AI Cloud活跃客户数同比去年增长65%
  3. iOS定位服务与地图应用开发:高德地图开发
  4. Zoom重金并购25人安全初创公司,市值一夜大涨23亿美元
  5. 【Flask】Jinja2模板之过滤器
  6. VMware View 与 Citrix Xendesktop 管理大比拼
  7. mysql数据库的快捷键
  8. 验证DetailsView插入数据不为空
  9. subtext blog装好了,老外的文档还是写的有些问题
  10. 马哥-Linux云计算架构班学习计划
  11. latex使用记录3
  12. vivo X30新细节曝光:搭载潜望式超远摄支持双模5G
  13. Netty工作笔记0058---Netty群聊系统客户端
  14. 中科视拓获千万pre-A轮融资,想做的不仅仅是人脸识别
  15. Codeforces Round #441 (Div. 2, by Moscow Team Olympiad)
  16. python 给qq点赞_Python实现QQ自动点赞
  17. 怎么看服务器cpu温度命令_ubuntu 命令行下查看及监测CPU温度的方法
  18. 自己整理mysql语句
  19. HTTP Cache(缓存)
  20. 【一行代码系列】Python 的多线程

热门文章

  1. cdn 导致跨域问题
  2. 何建坤:实施能源革命战略 促绿色低碳发展
  3. [BZOJ 4403]序列统计(Lucas定理)
  4. MySQL编译安装时常见错误分析
  5. pkcs1与pkcs8格式RSA私钥互相转换
  6. 动态修改ViewPagerIndicator CustomTabPageIndicator Tab标签文字颜色
  7. 你实现团队管理了吗?
  8. 聊天机器人语料批量处理-自动提取关键词并自动写入文件
  9. 05-树7 堆中的路径
  10. 【COCOS2DX-LUA 脚本开发之一】LUA语言基础在COCOS2DX游戏中使用LUA脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途!...