基于DFS的拓扑排序
传送门:Kahn算法拓扑排序
摘录一段维基百科上的伪码:
L ← Empty list that will contain the sorted nodes
S ← Set of all nodes with no outgoing edges
for each node n in S do
visit(n)
function visit(node n)
if n has not been visited yet then
mark n as visited
for each node m with an edgefrom m to ndo
visit(m)
add n to L
DFS的实现更加简单直观,使用递归实现。利用DFS实现拓扑排序,实际上只需要添加一行代码,即上面伪码中的最后一行:add
n to L。
需要注意的是,将顶点添加到结果List中的时机是在visit方法即将退出之时。
这个算法的实现非常简单,但是要理解的话就相对复杂一点。
关键在于为什么在visit方法的最后将该顶点添加到一个集合中,就能保证这个集合就是拓扑排序的结果呢?
因为添加顶点到集合中的时机是在dfs方法即将退出之时,而dfs方法本身是个递归方法,只要当前顶点还存在边指向其它任何顶点,它就会递归调用dfs方法,而不会退出。因此,退出dfs方法,意味着当前顶点没有指向其它顶点的边了,即当前顶点是一条路径上的最后一个顶点。
下面简单证明一下它的正确性:
考虑任意的边v->w,当调用dfs(v)的时候,有如下三种情况:
- dfs(w)还没有被调用,即w还没有被mark,此时会调用dfs(w),然后当dfs(w)返回之后,dfs(v)才会返回
- dfs(w)已经被调用并返回了,即w已经被mark
dfs(w)已经被调用但是在此时调用dfs(v)的时候还未返回
需要注意的是,以上第三种情况在拓扑排序的场景下是不可能发生的,因为如果情况3是合法的话,就表示存在一条由w到v的路径。而现在我们的前提条件是由v到w有一条边,这就导致我们的图中存在环路,从而该图就不是一个有向无环图(DAG),而我们已经知道,非有向无环图是不能被拓扑排序的。
那么考虑前两种情况,无论是情况1还是情况2,w都会先于v被添加到结果列表中。所以边v->w总是由结果集中后出现的顶点指向先出现的顶点。为了让结果更自然一些,可以使用栈来作为存储最终结果的数据结构,从而能够保证边v->w总是由结果集中先出现的顶点指向后出现的顶点。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<sstream>
#include<functional>
using namespace std;
typedef long long ll;
const int maxn = + ;
const int INF = 1e9 + ;
int T, n, m, cases;
vector<int>Map[maxn];
int c[maxn];//标记数组c[i] = 0 表示还未访问过点i, c[i] = 1表示已经访问过点i,并且还递归访问过它的所有子孙,c[i] = -1表示正在访问中,尚未返回
int topo[maxn], t;
bool dfs(int u)//从u出发
{
c[u] = -;//访问标志
for(int i = ; i < Map[u].size(); i++)
{
int v = Map[u][i];
if(c[v] < )return false;//如果子孙比父亲先访问,说明存在有向环,失败退出
else if(!c[v] && !dfs(v))return false;//如果子孙未被访问,访问子孙返回假,说明也是失败
}
c[u] = ;
topo[--t] = u;//在递归结束才加入topo排序中,这是由于在最深层次递归中,已经访问到了尽头,此时才是拓扑排序中的最后一个元素
return true;
}
bool toposort()
{
t = n;
memset(c, , sizeof(c));
for(int u = ; u <= n; u++)if(!c[u])
if(!dfs(u))return false;
return true;
}
int main()
{
while(cin >> n >> m)
{
if(!n && !m)break;
int u, v;
for(int i = ; i <= n; i++)Map[i].clear();
for(int i = ; i < m; i++)
{
cin >> u >> v;
Map[u].push_back(v);
}
if(toposort())
{
cout<<"Great! There is not cycle."<<endl;
for(int i = ; i < n; i++)cout<<topo[i]<<" ";
cout<<endl;
}
else cout<<"Network has a cycle!"<<endl;
}
return ;
}
基于DFS的拓扑排序的更多相关文章
- 日常训练 dfs 之 拓扑排序
今天被拓扑排序给折磨了一天,主要就是我的一个代码有点小bug,真难找... 先来看看我今天写的题目吧! C. Fox And Names Fox Ciel is going to publish a ...
- 拓扑排序(topsort)
本文将从以下几个方面介绍拓扑排序: 拓扑排序的定义和前置条件 和离散数学中偏序/全序概念的联系 典型实现算法解的唯一性问题 Kahn算法 基于DFS的算法 实际例子 取材自以下材料: http://e ...
- UVA-10305 Ordering Tasks (拓扑排序)
题目大意:给出n个点,m条关系,按关系的从小到大排序. 题目分析:拓扑排序的模板题,套模板. kahn算法: 伪代码: Kahn算法: 摘一段维基百科上关于Kahn算法的伪码描述: L← Empty ...
- 拓扑排序(基于dfs+基于队列)
经典问题-Ordering Tasks dfs函数的返回值表示是否成环,若存在有向环,则不存在拓扑排序.不包含有向环的有向图称为有向无环图(DAG) 可以借助DFS完成拓扑排序,在访问完一个结点时把他 ...
- HDU3342有向图判圈DFS&&拓扑排序法
HDU3342 Legal or Not 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3342 题目意思:一群大牛互相问问题,大牛有不会的,会被更厉害 ...
- 拓扑排序详解(梅开二度之dfs版按字典序输出拓扑路径+dfs版输出全部拓扑路径
什么是拓扑排序? 先穿袜子再穿鞋,先当孙子再当爷.这就是拓扑排序! 拓扑排序说白了其实不太算是一种排序算法,但又像是一种排序(我是不是说了个废话qwq) 他其实是一个有向无环图(DAG, Direct ...
- 算法笔记_023:拓扑排序(Java)
目录 1 问题描述 2 解决方案 2.1 基于减治法实现 2.2 基于深度优先查找实现 1 问题描述 给定一个有向图,求取此图的拓扑排序序列. 那么,何为拓扑排序? 定义:将有向图中的顶点以线性方式进 ...
- Java实现拓扑排序
1 问题描述 给定一个有向图,求取此图的拓扑排序序列. 那么,何为拓扑排序? 定义:将有向图中的顶点以线性方式进行排序.即对于任何连接自顶点u到顶点v的有向边uv,在最后的排序结果中,顶点u总是在顶点 ...
- 有向无环图的应用—AOV网 和 拓扑排序
有向无环图:无环的有向图,简称 DAG (Directed Acycline Graph) 图. 一个有向图的生成树是一个有向树,一个非连通有向图的若干强连通分量生成若干有向树,这些有向数形成生成森林 ...
随机推荐
- 第八届蓝桥杯省赛17【java B组】第一题
1,标题: 购物单 小明刚刚找到工作,老板人很好,只是老板夫人很爱购物.老板忙的时候经常让小明帮忙到商场代为购物.小明很厌烦,但又不好推辞. 这不,XX大促销又来了!老板夫人开出了长长的购 ...
- node命令curl
一.打开另一个命令行窗口,运行下面的命令. curl -X POST --data "name=Jack" 127.0.0.1:3000 上面代码使用 POST 方法向服务器发送一 ...
- linux --> 为什么寄存器比内存快?
为什么寄存器比内存快 计算机的存储层次(memory hierarchy)之中,寄存器(register)最快,内存其次,最慢的是硬盘. 原因一:距离不同 距离不是主要因素,但是最好懂,所以放在最前面 ...
- 斑马ZPL指令加入如换行、回车等控制符的方法
在程序中可能会被过滤掉,直接在指令中加入ASCII对应的16进制字符即可解决改问题, 语法:_十六进制(ASCII) 栗子:hello_0D_0Aworld 换行 扫描结果: hello world
- Oracle查询优化改写--------------------单表查询
一.查询表中所有的行与列 二.从表中检索部分行 三.查找空值 四.将空值转化为实际值(coalesce) 五.查找满足多个条件的行(查询部门为10中所有的员工.所有得到提成的员工,以及部门20中工资不 ...
- kvm之四:从网上镜像安装虚拟机Centos6.8
1.再加块硬盘,格式化挂载至新建目录/kvm2下 2.CentOS 6.8镜像地址 http://mirrors.163.com/centos/6.8/os/x86_64/ 3.配置安装参数,执行安装 ...
- div内文字显示两行,多出的文字用省略号显示
用-webkit-私有属性,代码如下:text-overflow: -o-ellipsis-lastline;overflow: hidden;text-overflow: ellipsis;disp ...
- 2018上C语言程序设计(高级)作业- 初步计划
C语言程序设计(高级)36学时,每周4学时,共9周.主要学习指针.结构和文件三部分内容.整个课程作业计划如下: PTA和博客的使用指南 若第一次使用PTA和博客,请务必先把PTA的使用简介和教师如何在 ...
- C语言第五次作业函数
一.PTA实验作业 题目1: 6-6 使用函数输出水仙花数 1.本题PTA提交列表 2.设计思路 1.narcissistic函数 1.由于number的值后面会变化,所以定义d,e用于储存numbe ...
- 代码中输入数字自动筛选出最大值,使用array,for loop and if (21.9.2017)
# include <stdio.h> # define N main(){ int a, b; ,,,,,,,,,,,,,,,,}; //array中输入需要排序的数字 ]; ; a & ...