算法学习 拓扑排序(TopSort)
拓扑排序
一、基本概念
在一个有向无环图(Directed Acyclic Graph, DAG)中,规定< u,v > 表示一条由u指向v的的有向边。要求对所有的节点排序,使得每一条有向边 < u,v>中u都排在v的前面。
换个形象点的解释,我们在学习一门课程之前,应该需要一定的预备知识,比如在学习B课程之前我们需先学习A(后用< X,Y > 表示X课程是Y课程的预备知识,其实与上述有序偶的含义相同),则有 < A,B >。我们还有 < C,B >, < B,D >, < E,D >, < D,F >, < D,G >, < H,G >. 现在要求你合理安排A-H这些课程的学习顺序。这个任务的要求实际上就是对A-H进行拓扑排序。
为何要求这个图是DAG呢?不难想象,如果上面的课程变成 < A,B >, < B,C >, < C,D > …… < H,A >. (即A-H成有向环)那么我们就无法判断出先学哪个课程了。
二、算法实现
以上面给课程排序为例,我们首先要学的,一定是一个不需要任何预备知识的课程,然后学完这个课程之后,根据边的关系再看有哪些新的课程可以学习,同时我们还要清楚,学完一门课程后,就不需要再次学习这一门课程了。
我们将其与图做个类比。
不需要预备知识的课程-> 入度为0的点
新的课程->所指向的下一个点
每门课之学一次->从图中删除节点 && 从图中删除有向边
接着再根据图类比结果决定存储的数据
入度为0的点->需要存储每个节点的入度
所指向的下一个点->需要存每个节点的后继
删除节点与有向边-> 这里讨论一下:
如果我们真的删除了节点和有向边,那就意味着无法在找到这些数据了。因为我们还需要判断是否形成了DAG,最保险的做法是将下一个节点的入度-1。如果发现某个节点的入度为-1了,表明存在有向环,那么说明不存在与拓扑排序。
根据存储的数据选择数据结构(具体情况具体分析,灵活变通)
节点入度->数组
节点后继->vector
三、例题讲解
UVA.10305 Ordering Tasks
有n个点,m条边,给n个顶点做拓扑排序。
#include <iostream>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <sstream>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
#define nmax 200
#define MEM(x) memset(x,0,sizeof(x))
using namespace std;
vector<int> v[nmax];
int indegree[nmax];
int n;
bool suc = true;
queue<int> ans;
void topsort()
{
queue<int> q;
while(1){
for(int i = 1; i<=n ;++i){
if(indegree[i] == 0){
q.push(i);
ans.push(i);
indegree[i] = -1;
}
}
if(q.empty()) break;
while(!q.empty()){
int t = q.front(); q.pop();
for(int j = 0;j<v[t].size();++j){
int tt = v[t][j];
if(indegree[tt] == -1){
suc = false;
break;
}else indegree[tt]--;
}
v[t].clear();
if(!suc) break;
}
if(!suc) break;
}
if(ans.size() <n){
suc =false;
return;
}
}
void output()
{
bool isfirst = true;
while(!ans.empty()){
int t = ans.front(); ans.pop();
if(isfirst){
printf("%d",t);
isfirst = false;
}else
printf(" %d",t);
}
printf("\n");
}
int main()
{
//freopen("in.txt","r",stdin);
int m;
while(scanf("%d%d",&n,&m) ==2 && (n||m)){
MEM(indegree);
suc = true;
int a,b;
for(int i = 0; i<m; ++i){
scanf("%d%d",&a,&b);
indegree[b]++;
v[a].push_back(b);
}
topsort();
if(suc) output();
else printf("failed\n");
}
return 0;
}
基本方法是,indegree表示入度表,vector存后继节点。在topsort函数中,制造一个辅助队列,首先从入度表中找到入度为0的点作起点,并且置入度为-1。接着依次处理队列中的节点,首先根据他们的后继,将其后继节点的入度依次减1,若其后继节点中的入度存在-1的,说明成环,则不存在拓扑排序。紧接着再从入度表中找到入度为0的节点,加入到队列中,直到队列空。当退出while循环的时候,需要检查ans答案队列中是否已经有全部的节点,若其数量为n,则表明存在拓补排序,否则不存在。
当然本题满足存在topsort。下面给出三组例子,可以检验一下自己程序的健壮性。
Graph1
不存在拓扑排序,节点2,3,4成环
4 5
1 3
1 2
2 3
3 4
4 2
Graph2
不存在拓扑排序,节点1,2,3成环
5 6
5 1
1 2
2 3
3 1
1 4
4 3
Graph3
存在拓扑排序
13 14
1 2
1 6
1 7
3 1
3 4
4 6
6 5
8 7
9 8
7 10
10 11
10 12
10 13
12 13
算法学习 拓扑排序(TopSort)的更多相关文章
- HDU.3342 Legal or Not (拓扑排序 TopSort)
HDU.3342 Legal or Not (拓扑排序 TopSort) 题意分析 裸的拓扑排序 根据是否成环来判断是否合法 详解请移步 算法学习 拓扑排序(TopSort) 代码总览 #includ ...
- HDU.2647 Reward(拓扑排序 TopSort)
HDU.2647 Reward(拓扑排序 TopSort) 题意分析 裸的拓扑排序 详解请移步 算法学习 拓扑排序(TopSort) 这道题有一点变化是要求计算最后的金钱数.最少金钱值是888,最少的 ...
- HDU.1285 确定比赛名次 (拓扑排序 TopSort)
HDU.1285 确定比赛名次 (拓扑排序 TopSort) 题意分析 裸的拓扑排序 详解请移步 算法学习 拓扑排序(TopSort) 只不过这道的额外要求是,输出字典序最小的那组解.那么解决方案就是 ...
- 拓扑排序 topsort详解
1.定义 对一个有向无环图G进行拓扑排序,是将G中所有顶点排成一个线性序列,通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列. 举例: h3 { marg ...
- NetworkX系列教程(10)-算法之四:拓扑排序与最大流问题
小书匠Graph图论 重头戏部分来了,写到这里我感觉得仔细认真点了,可能在NetworkX中,实现某些算法就一句话的事,但是这个算法是做什么的,用在什么地方,原理是怎么样的,不清除,所以,我决定先把图 ...
- 拓扑排序 topsort
拓扑排序 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序 ...
- 算法学习记录-排序——插入排序(Insertion Sort)
插入排序: 在<算法导论>中是这样描述的 这是一个对少量元素进行排序的有效算法.插入排序的工作机理与打牌时候,整理手中的牌做法差不多. 在开始摸牌时,我们的左手是空的,牌面朝下放在桌子上. ...
- 算法学习记录-排序——冒泡排序(Bubble Sort)
冒泡排序应该是最常用的排序方法,我接触的第一个排序算法就是冒泡,老师也经常那这个做例子. 冒泡排序是一种交换排序, 基本思想: 通过两两比较相邻的记录,若反序则交换,知道没有反序的记录为止. 例子: ...
- 【算法学习记录-排序题】【PAT A1012】The Best Rank
To evaluate the performance of our first year CS majored students, we consider their grades of three ...
随机推荐
- RabbitMQ基础教程之Spring&JavaConfig使用篇
RabbitMQ基础教程之Spring使用篇 相关博文,推荐查看: RabbitMq基础教程之安装与测试 RabbitMq基础教程之基本概念 RabbitMQ基础教程之基本使用篇 RabbitMQ基础 ...
- Selenium自动化测试第一天(上)
如有任何学习问题,可以添加作者微信:lockingfree 目录 Selenium自动化测试基础 Selenium自动化测试第一天(上) Selenium自动化测试第一天(下) Selenium自动化 ...
- laravel 的DB::raw() 语法使用
z之前在项目中遇到一个问题,复杂的sql查询,用laravel的查询构造器,非常的不方便,各种查询条件拼接一长串拼得脑瓜疼:然后想使用原生的sql语句来查询,然后又使用不了laravel的pagina ...
- leetcode7_C++整数反转
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转. 示例 1: 输入: 输出: 示例 2: 输入: - 输出: - 示例 3: 输入: 输出: 注意: 假设我们的环境只能存 ...
- python数据文件读写
CSV格式读写 Comma-Separated Values 有时也称为字符分隔值,因为分隔字符也可以不是逗号.以,分隔的文件叫csv,以\t分隔的叫tsv 需要注意的一点:分隔符 import cs ...
- [转载] RCNN/SPP/FAST RCNN/FASTER RCNN/YOLO/SSD算法简介
RCNN: RCNN(Regions with CNN features)是将CNN方法应用到目标检测问题上的一个里程碑,由年轻有为的RBG大神提出,借助CNN良好的特征提取和分类性能,通过Regio ...
- windows10下git一些问题
windows10下安装git 找不到ssh解决办法 解决办法是: 输入下列命令,一路回车 $ ssh-keygen -t rsa -C “邮箱地址” 若执行ssh-add /path/to/xxx. ...
- es6从零学习(一)let 和 const 命令
es6从零学习(一):let 和 const 命令 一:let 变量 1.块级作用域{}:let只在自己的块级作用域内有效. for(let i =0;i<3;i++) { console.lo ...
- 第四课——MFC应用程序框架
一.MFC应用程序类型 上篇文章的彩蛋:可通过使用MFC应用程序向导(MFC AppWizard)的功能来创建所需要的应用程序,这意味着不需要输入任何代码.MFC除了应用程序向导,还对应用程序项目有着 ...
- Coins and Queries(map迭代器+贪心)
题意 n个硬币,q次询问.第二行给你n个硬币的面值(保证都是2的次幂!).每次询问组成b块钱,最少需要多少个硬币? Example Input 5 42 4 8 2 4851410 Output 1- ...