拓扑排序1

合集下载

拓扑排序-有向无环图(DAG,DirectedAcyclicGraph)

拓扑排序-有向无环图(DAG,DirectedAcyclicGraph)

拓扑排序-有向⽆环图(DAG,DirectedAcyclicGraph)条件:1.每个顶点出现且只出现⼀次。

2.若存在⼀条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前⾯。

有向⽆环图(DAG)才有拓扑排序,⾮DAG图没有拓扑排序⼀说。

⼀般⽤有向边指⽰顺序关系,运⽤于顺序关系。

例如,下⾯这个图:显然是⼀个DAG图,1→4表⽰4的⼊度+1,4是1的邻接点,代码表⽰:前者deg[4]++;后者⽤vector[1].push(4)如何写出拓扑排序代码?1.⾸先将边与边的关系确定,建⽴好⼊度表和邻接表。

2.从⼊度为0的点开始删除,如上图显然是1的⼊度为0,先删除。

3.于是,得到拓扑排序后的结果是 { 1, 2, 4, 3, 5 }。

通常,⼀个有向⽆环图可以有⼀个或多个拓扑排序序列。

因为同⼀⼊度级别的点可以有不同的排序⽅式。

4.拓扑排序可以判断图中有⽆环,如下图显然4,5,6⼊度都是1,不存在⼊度为0的点,⽆法进⾏删除操作。

判断有⽆环的⽅法,对⼊度数组遍历,如果有的点⼊度不为0,则表明有环。

例题+代码拓扑排序(⼀)-Hiho-Coder1174描述由于今天上课的⽼师讲的特别⽆聊,⼩Hi和⼩Ho偷偷地聊了起来。

⼩Ho:⼩Hi,你这学期有选什么课么?⼩Hi:挺多的,⽐如XXX1,XXX2还有XXX3。

本来想选YYY2的,但是好像没有先选过YYY1,不能选YYY2。

⼩Ho:先修课程真是个⿇烦的东西呢。

⼩Hi:没错呢。

好多课程都有先修课程,每次选课之前都得先查查有没有先修。

教务公布的先修课程记录都是好多年前的,不但有重复的信息,好像很多都不正确了。

⼩Ho:课程太多了,教务也没法整理吧。

他们也没法⼀个⼀个确认有没有写错。

⼩Hi:这不正是轮到⼩Ho你出马的时候了么!⼩Ho:哎??我们都知道⼤学的课程是可以⾃⼰选择的,每⼀个学期可以⾃由选择打算学习的课程。

唯⼀限制我们选课是⼀些课程之间的顺序关系:有的难度很⼤的课程可能会有⼀些前置课程的要求。

拓扑排序---AOV图

拓扑排序---AOV图

拓扑排序---AOV图对⼀个有向⽆环图(Directed Acyclic Graph简称DAG)G进⾏拓扑排序,是将G中全部顶点排成⼀个线性序列,使得图中随意⼀对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出如今v之前。

通常,这种线性序列称为满⾜拓扑次序(Topological Order)的序列,简称拓扑序列。

简单的说。

由某个集合上的⼀个偏序得到该集合上的⼀个全序,这个操作称之为拓扑排序.步骤:由AOV⽹构造拓扑序列的拓扑排序算法主要是循环运⾏下⾯两步,直到不存在⼊度为0的顶点为⽌。

(1) 选择⼀个⼊度为0的顶点并输出之。

(2) 从⽹中删除此顶点及全部出边。

循环结束后,若输出的顶点数⼩于⽹中的顶点数。

则输出“有回路”信息,否则输出的顶点序列就是⼀种拓扑序列.代码段例如以下://拓扑排序图AOE代码段//杨鑫/**在⼀个表⽰project的有向图。

⽤顶点表⽰活动,⽤弧表⽰活动之间的优先关系,*这种有向图为顶点表⽰活动的⽹。

我们称为AOV(Activity On Vertex Network)* *///边表结点#define MAXVEX 1000typedef struct EdgeNode{int adjvex; //邻接点域。

存储该顶点相应的下标int weight; //⽤于存储权值,对于⾮⽹图能够不须要struct EdgeNode *next; //链域。

指向下⼀个邻接点}EdgeNode;//顶点表结点typedef struct VertexNode{int in; //顶点的⼊度int data; //顶点域,存储顶点信息EdgeNode *firstedge; //边表头指针}VertexNode, AdjList[MAXVEX];typedef struct{AdjList adjList;int numVertexes, numEdges; //图中当前的顶点数和边数}graphAdjList, *GraphAdjList;//此处还应该定义⼀个栈来存储⼊度为0的顶点。

数据结构(牛小飞)3 拓扑排序

数据结构(牛小飞)3 拓扑排序

2021/8/5
25
5
拓扑排序-定义
拓扑排序是对有向无圈图的顶点的一种排序,使 得如果存在一条从vi到vj的路径,那么在排序中vj 就出现在vi的后面。
✓ 显然,如果图中含有圈,那么拓扑排序是不可能的, 因为对于圈上的两个顶点v和w,v优先于w同时w又优 先于v。
2021/8/5
6
拓扑排序-举例
B
A
D
C
不能求得它的拓扑有序序列。
// 对尚未访问的顶点调用DFS
}
while(!Empty(S)) //输出拓扑排序的结果
System.out.print(S.pop());
} 2021/8/5
21
拓扑排序-方法2
void DFS-T(int v) { // 从顶点v出发,深度优先搜索遍历连通图 vertexs[v].visited = true; for(w=FirstAdjVex(v);w>=0; w=NextAdjVex(v,w)) { if (!vertexs[w].visited) DFS-T(w); } // 对v的尚未访问的邻接顶点w递归调用DFS-T S.push(v); //顶点v的DFS函数执行完毕
q.enqueue(v);
while (!q.isEmpty( ) { //如果队列非空
…………
}
if (counter!=NUM_VERTICES) //有圈
throw new CycleFoundException( );
}
2021/8/5
15
拓扑排序-方法1
void topsort( ) throws CycleFoundException { …….
} // DFS-T

拓扑排序例子 -回复

拓扑排序例子 -回复

拓扑排序例子-回复什么是拓扑排序?为什么要进行拓扑排序?如何进行拓扑排序?这篇文章将为你逐步解答这些问题。

拓扑排序是一种用于有向无环图(DAG)的算法,它将图中的节点按照一种特定的顺序进行排序。

拓扑排序往往用于解决有依赖关系的任务调度问题,其中任务的执行必须按照一定的顺序。

通过拓扑排序,我们可以确定任务的执行顺序,确保每个任务在它所依赖的任务执行完毕之后再执行。

那么为什么要进行拓扑排序呢?想象一下,你要为一本书编写目录,你希望按照章节的顺序来编写目录,否则读者可能会迷失在无序的章节中。

在这个例子中,每个章节都相当于一个任务,它们之间存在一定的依赖关系,因为后续的章节可能会引用之前的内容。

通过拓扑排序,你可以确定每个章节的先后顺序,确保目录的编写顺利进行。

那么如何进行拓扑排序呢?拓扑排序使用了一种叫做“深度优先搜索”的算法。

算法从一个未访问的节点开始遍历图,当遍历到一个节点时,它将其标记为已访问,并继续遍历该节点的所有邻接节点。

当一个节点的所有邻接节点都被访问完毕后,它将被添加到排序结果的头部。

最终,我们得到的排序结果就是拓扑排序的结果。

下面,我们来举一个具体的例子来演示拓扑排序的过程。

假设我们有以下的任务依赖关系图:[1] > [2] > [4]\\ v> [3] > [5]在这个图中,节点表示任务,箭头表示任务之间的依赖关系。

例如,节点2依赖于节点1,节点4依赖于节点2,等等。

我们的目标是按照依赖关系的顺序对节点进行排序。

首先,我们从一个未访问的节点开始,假设我们选择节点1。

我们接下来需要遍历节点1的所有邻接节点,即节点2和节点3。

然后,我们继续遍历这两个节点的邻接节点,依次为节点4和节点5。

由于节点4和节点5都没有邻接节点,它们被添加到排序结果的头部。

接着,我们回溯到节点2,它的邻接节点3已经被访问过了,所以它也被添加到排序结果的头部。

最后,我们回溯到节点1,将其添加到排序结果的头部。

拓扑排序序列的步骤

拓扑排序序列的步骤

拓扑排序序列的步骤拓扑排序是一种常用的有向图排序算法,它可以用来解决依赖关系的排序问题。

拓扑排序序列指的是通过拓扑排序算法得到的图中节点的一个线性排序。

在本文中,我们将深入探讨拓扑排序的步骤并给出实现示例。

一、拓扑排序简介拓扑排序适用于有向无环图(DAG)。

它的基本思想是将有向图中的节点按照依赖关系排序,使得每个节点的所有前驱节点都在它的前面。

如果存在环路,则无法进行拓扑排序。

二、拓扑排序步骤1. 初始化一个队列,用于储存入度为0的节点。

2. 遍历图中的所有节点,并统计每个节点的入度,将入度为0的节点加入队列。

3. 从队列中取出一个节点,将其输出,并将其所有邻接节点的入度减1。

4. 如果邻接节点的入度变为0,则将其加入队列。

5. 重复步骤3和步骤4,直到队列为空。

6. 如果输出的节点数量与图中节点的数量相同,则拓扑排序成功;否则,说明图中存在环路,无法进行拓扑排序。

示例代码如下:```pythondef topological_sort(graph):indegree = [0] * len(graph)queue = []# 统计每个节点的入度for node in graph:for adjacent in graph[node]: indegree[adjacent] += 1 # 将入度为0的节点加入队列 for i in range(len(indegree)):if indegree[i] == 0:queue.append(i)result = []while queue:node = queue.pop(0)result.append(node)# 将邻接节点的入度减1 for adjacent in graph[node]:indegree[adjacent] -= 1if indegree[adjacent] == 0: queue.append(adjacent) # 判断是否成功拓扑排序if len(result) == len(graph):return resultelse:return []# 图的邻接表表示graph = {0: [1, 2],1: [3, 4],2: [3],3: [],4: [3]}result = topological_sort(graph)if result:print("拓扑排序序列为:", result)else:print("图中存在环路,无法进行拓扑排序")```以上代码实现了拓扑排序的步骤,可以根据具体需求进行调用和扩展。

拓扑排序的基本算法

拓扑排序的基本算法

拓扑排序的基本算法拓扑排序是面试中经常涉及到的一个经典算法,特别是在有向无环图(Directed Acyclic Graph, DAG)的处理中应用广泛。

本文将详细介绍拓扑排序的基本算法及其实现过程。

1. 前置知识在讲拓扑排序算法之前,需要先理解有向图和拓扑结构的概念。

有向图:顶点之间存在有向边的图。

拓扑结构:由一组具有局部顺序关系的节点组成的集合,这些节点既可以是实际存在的物体,也可以是抽象的概念。

2. 拓扑排序的定义拓扑排序是针对有向无环图(DAG)所定义的一种算法,它可以将DAG图中的所有节点排成一条线性序列,使得对于任何一条边(u,v),都有u排在v的前面。

3. 拓扑排序的基本过程步骤一:选择入度为0的节点作为起点。

如果没有入度为0的节点,则图中存在环,算法无法完成。

步骤二:对选择的起点进行遍历,将它所能到达的节点的入度减一。

步骤三:再从入度为0的节点开始,重复上述过程,直到所有节点都已经遍历完成。

4. 拓扑排序的实现方式使用队列来实现拓扑排序过程。

4.1 准备阶段:- 定义一个队列queue和一个数组inDegree用来存储每个节点的入度;- 将每个节点的入度初始化为0;- 遍历有向图,计算每个节点的入度,并更新inDegree数组。

4.2 开始拓扑排序:- 将所有入度为0的节点放入队列中;- 当队列非空时,重复下述操作:1. 取出队首元素;2. 对该节点能够到达的所有节点的入度减一;3. 如果入度减为0,则将该节点加入队列中。

- 如果拓扑排序过程中遍历到的节点数小于有向图的节点总数,则有向图中存在环。

5. 拓扑排序的时间复杂度和空间复杂度时间复杂度为O(|V|+|E|),其中V表示节点集合,E表示边集合。

空间复杂度为O(|V|),即需要存储每个节点的入度。

6. 总结拓扑排序算法是面试中必备的经典算法,它能够处理有向无环图(DAG)中的节点排序问题,为后续的遍历等操作提供了便利。

拓扑排序的基本过程包括选择入度为0的节点、遍历节点及更新节点的入度,使用队列来实现算法过程。

什么是拓扑排序?

什么是拓扑排序?

什么是拓扑排序?
拓扑排序是一种对有向无环图(DAG)进行排序的算法,它可以将图中的顶点
按照一定的顺序排列,使得图中任意一条边的起点在排列中都出现在终点之前。

具体来说,拓扑排序的过程是这样的:
1. 首先,找到图中入度为0的顶点(即没有任何边指向它的顶点),将其加入到排序的结果中。

2. 然后,移除这个顶点以及由它出发的所有边,更新剩余顶点的入度。

3. 重复以上步骤,直到所有的顶点都被加入到排序结果中或者发现图中存在环。

如果最终所有的顶点都被加入到排序结果中,那么这个排序就是图的一个拓扑
排序;如果在过程中发现了环,那么图不具有拓扑排序。

拓扑排序的应用非常广泛,比如在软件工程中可以用来解决模块的依赖关系,
或者在任务调度中确定任务的执行顺序等等。

这个算法的时间复杂度为O(V+E),其中V为顶点的数量,E为边的数量。

拓扑排序及关键路径

拓扑排序及关键路径

2.有向图在实际问题中的应用 一个有向图可以表示一个施工流程图,或产品生产流程
图,或数据流图等。设图中每一条有向边表示两个子工程之 间的先后次序关系。
若以有向图中的顶点来表示活动,以有向边来表示活动 之间的先后次序关系,则这样的有向图称为顶点表示活动的 网 (Activity On Vertex Network),简称AOV网。
这样,每个活动允许的时间余量就是l(i) - e(i)。而关键活动 就是l(i) - e(i) = 0的那些活动,即可能的最早开始时间e(i)等于 允许的最晚开始时间l(i)的那些活动就是关键活动。
4.寻找关键活动的算法 求AOE网中关键活动的算法步骤为: (1)建立包含n+1个顶点、e条有向边的AOE网。其中,顶
(4)从汇点vn开始,令汇点vn的最晚发生时间vl[n]=ve[n], 按逆拓扑序列求其余各顶点k(k=n-1,n-2,…,2,1,0)的最晚发生 时间vl[k];
(5)计算每个活动的最早开始时间e[k] (k=1,2,3,…,e); (6)计算每个活动的最晚开始时间l[k] (k=1,2,3,…,e); (7)找出所有e[k]= l[k]的活动k,这些活动即为AOE网的 关键活动。
上述算法仅能得到有向图的一个拓扑序列。改进上述 算法,可以得到有向图的所有拓扑序列。
如果一个有向图存在一个拓扑序列,通常表示该有向 图对应的某个施工流程图的一种施工方案切实可行;而 如果一个有向图不存在一个拓扑序列,则说明该有向图 对应的某个施工流程图存在设计问题,不存在切实可行 的任何一种施工方案。
事件可能的最早开始时间υe(k):对于顶点υk代表的事件, υe(k)是从源点到该顶点的最大路径长度。在一个有n+1个事 件的AOE网中, 源点υ0的最早开始时间υe(0)等于0。事件υk (k=1,2,3,…,n)可能的最早开始时间υe(k)可用递推公式表 示为:
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
//--------------------初始化图 void GAdjoin::InitAdjoin() {
int v,e; cout<<endl; cout<<"请输入顶点数:"; cin>>v; VertexNum=v; cout<<endl; for(int i=0;i<=v;i++) {
adjlist[i]=NULL; } cout<<"请输入边数:"; cin>>e; EdgeNum=e;
二 概要设计 1. 算法中用到的所有各种数据类型的定义
在该程序中用邻接表作为图的存储结构。首先,定义表结点和头结点的结构 类型,然后定义图的结构类型。创建图用邻接表存储的函数,其中根据要求输入 图的顶点和边数,并根据要求设定每条边的起始位置,构建邻接表依次将顶点插 入到邻接表中。 拓扑排序的函数在该函数中首先要对各顶点求入度,其中要用到求入度的函数, 为了避免重复检测入度为零的顶点,设置一个辅助栈,因此要定义顺序栈类型, 以及栈的函数:入栈,出栈,判断栈是否为空。
2.3 void Push(SqStack *S,ElemType e) 进栈操作,插入元素 e 为新的栈顶元素。
2.4 int StackEmpty(SqStack *S) 判断栈是否为空,语句 if (S->top=S->base )判断,若栈不为空,则删除 S 的
栈顶元素,并返回 OK;否则返回 ERROR。 2.5 void CreatGraph (ALGraph *G)
为了避免重复检测入度为零的顶点,可以再设置一个辅助栈,若某一顶点 的入度减为 0,则将它入栈。每当输出某一入度为 0 的顶点时,便将它从栈中删 除。 3 基本要求
(1) 输入的形式和输入值的范围; 首先是输入要排序的顶点数和弧数,都为整型,中间用分隔符隔开;再输
入各顶点的值,为正型,中间用分隔符隔开;然后输入各条弧的两个顶点值, 先输入弧头,再输入弧尾,中间用分隔符隔开,输入的值只能是开始输入的顶 点值否则系统会提示输入的值的顶点值不正确,请重新输入,只要继续输入正 确的值就行。 (2) 输出的形式;
int adjvex; edgenode *next;
};
class GAdjoin { public:
void InitAdjoin();//--------------------初始化图 void CreateAdjoin();//---------------创建图的邻接表 void PrintAdjoin();//-------------输出图的邻接表 void Toposort(); //------------输出图的拓扑序列 private: int MaxValue; int VertexNum; int EdgeNum; bool visited[50]; edgenode* adjlist[50]; edgenode*p; };
FindInDegree( G, indegreee[])对各顶点求入度;为了避免重复检测入度为零 0 的
顶点,设置一个栈,调用 InitStack(&S)初始化栈,在调用 Push(&S,i)入度为 0 者 进栈,while(!StackEmpty(&S))栈不为空时,调用 Pop(&sS,&n)输出栈中顶点并将 以该顶点为起点的边删除,入度 indegree[k]--,当输出某一入度为 0 的顶点时,便 将它从栈中删除。 3.算法的时间复杂度和空间复杂度
//--------------------初始化图 void GAdjoin::InitAdjoin() {
int v,e; cout<<endl; cout<<"请输入顶点数:"; cin>>v; VertexNum=v; cout<<endl; for(int i=0;i<=v;i++) {
拓扑排序的基本思想为: 1).从有向图中选一个无前驱的顶点输出; 2).将此顶点和以它为起点的弧删除; 3). 重复 1),2)直到不存在无前驱的顶点; 4). 若此时输出的顶点数小于有向图中的顶点数,则说明有向图中存在回 路,否则输出的顶点的顺序即为一个拓扑序列。 2.基于邻接表的存储结构 入度为零的顶点即为没有前驱的顶点, 我们可以附设一个存放各顶点入度 的数组 indegree[ ],于是有 (1)找 G 中无前驱的顶点——查找 indegree[i]为零的顶点 vi; (2)删除以 i 为起点的所有弧——对链在顶点 i 后面的所有邻接顶点 k,将 对应的 indegree[k] 减 1。
indegreee[i]=0 赋初值,for 循环 indegreee[]++,存储入度数。 2.7 void TopologicalISort(ALGraph G)
输出拓扑排序函数。其思路是若 G 无回路,则输出 G 的顶点的一个拓扑序 列并返回 OK,否则返回 ERROR。首先由于邻接表的存储结构入度为零的顶点 即为没有前驱的顶点,我们可以附设一个存放个顶点入度的数组,调用
在设计中,我们遇到了程序正确,却对某些无向图无法进行拓扑排序的问题。多 次 对 程 序 进 行 修 改 后 ,才 可 以 进 行 拓 扑 排 序 。问 题 出 在 调 用 函 数 的 错 误 理 解 ,模 块 之 间的联系模糊不清。
六附录: 源程序清单
#include<iostream> #include<iomanip> using namespace std; struct edgenode //------邻接表中的边节点类型 {
构建图,用邻接表存储,首先定义邻接表指针变量,输入顶点数和弧数,
初始化邻接表,将表头向量域置空,输入存在弧的点集合,当输入顶点值超出输
入值的范围就会出错,否则依次插入进邻接表,最后输出建立好的邻接表。
2.6 void FindInDegree(ALGrap G, int indegreee[]) 求入度操作,设一个存放各顶点入度的数组 indegreee[],然后
五 总结 拓扑排序就是对一个有向无环图(Directed Acyclic Graph 简称 DAG)G 进行拓扑排序,是
将 G 中所有顶点排成一个线性序列,使得图中任意一对顶点 u 和 v,若<u,v> ∈E(G),则 u 在线性序列中出现在 v 之前。
在进行课程设计中,更好的认识了拓扑排序。理清了各个模块之间算法之间的条理。认 识了 伪代码(Pseudocode)是一种算法描述语言。使用伪代码的目的是为了使被描 述 的 算 法 可 以 容 易 地 以 任 何 一 种 编 程 语 言( Pascal ,C,Java ,etc )实 现 。因 此 ,伪 代 码必须结构清晰、代码简单、可读性好,并且类似自然语言。 介于自然语言与编程 语 言 之 间 。它 是 一 种 让 人 便 于 理 解 的 代 码 。不 依 赖 于 语 言 的 ,用 来 表 示 程 序 执 行 过 程 , 而不一定能编译运行的代码。在数据结构讲算法的时候用的很多。
1.实现概要设计中定义的所有数据类型
#include<iostream> #include<iomanip> using names邻接表中的边节点类型 {
int adjvex; edgenode *next; };
class GAdjoin { public:
//-------------输出图的邻接表 void GAdjoin::PrintAdjoin() {
for(int i=0;i<VertexNum;i++){ cout<<'v'<<i; p=adjlist[i]; while(p!=NULL){ cout<<"->"<<p->adjvex; p=p->next; }
第三部分,主函数,先后调用 void CreatGraph(ALGraph *G)函数构建图、 void TopologicalSort(ALGraph *G)函数输出拓扑排序实现整个程序。 3.设计的主程序流程(见附页)
三 详细设计
(实现概要设计中定义的所有数据类型,对每个操作写出伪码算法;对主 程序和其他模块也都需要写出伪码算法(伪码算法达到的详细程度建议为;按照 伪码算法可以在计算机键盘直接输入高级程序设计语言程序);写出出函数和过 程的调用关系。)
首先输出建立的邻接表,然后是最终各顶点的出度数,再是拓扑排序的序 列,并且每输出一个顶点,就会输出一次各顶点的入度数。
(3) 程序所能达到的功能; 因为该程序是求拓扑排序,所以算法的功能就是要输出拓扑排序的序列,
在一个有向图中,若用顶点表示活动,有向边就表示活动间先后顺序,那么输出 的拓扑序列就表示各顶点间的关系为反映出各点的存储结构,以邻接表存储并输 出各顶点的入度。
}
//---------------创建图的邻接表 void GAdjoin::CreateAdjoin() {
int fromvex,endvex,e; cout<<endl; for(e=0;e<EdgeNum;e++){
cout<<"请输入第"<<e+1<<"条边(两端点序号,空格开):"; cin>>fromvex>>endvex; p=new edgenode; p->adjvex=endvex; p->next=adjlist[fromvex]; adjlist[fromvex]=p; } }
2.各程序模块之间的层次调用关系 第一部分,void CreatGraph(ALGraph *G)函数构建图,用邻接表存储。这个
相关文档
最新文档