有向图拓扑排序算法的实现
拓扑排序了解拓扑排序的概念和实现方式
拓扑排序了解拓扑排序的概念和实现方式拓扑排序:了解拓扑排序的概念和实现方式拓扑排序是一种用于有向无环图(DAG)中对节点进行排序的算法,它将图中所有节点按照一种线性排序的方式排列,使得任意一条有向边从排在前面的节点指向排在后面的节点。
本文将介绍拓扑排序的概念和实现方式。
一、拓扑排序的概念拓扑排序的概念是基于拓扑排序图的。
拓扑排序图是一个有向图,其中每个节点表示一个任务,有向边表示任务之间的依赖关系。
如果任务A依赖于任务B完成,那么在拓扑排序图中就有一条从B指向A的有向边。
拓扑排序的目标是找到一种任务排序的方式,使得排在前面的任务不依赖于排在后面的任务。
这样,我们可以按照拓扑排序中节点的顺序依次执行任务,确保任务的依赖关系得以满足。
二、拓扑排序的实现方式拓扑排序有两种主要的实现方式:Kahn算法和深度优先搜索(DFS)算法。
1. Kahn算法Kahn算法是一种基于贪心策略的拓扑排序算法,它通过不断删除没有入度的节点来构建拓扑排序。
Kahn算法的实现步骤如下:步骤1:初始化一个队列Q,并将所有入度为0的节点加入队列Q。
步骤2:当队列Q非空时,执行以下操作:1)从队列Q中取出一个节点u;2)将节点u添加到排序结果中;3)遍历u的所有邻接节点v,将v的入度减1;4)若节点v的入度减为0,则将节点v加入队列Q。
步骤3:返回排序结果。
2. 深度优先搜索算法深度优先搜索(DFS)是另一种常用的拓扑排序算法。
它通过递归地访问节点和其邻接节点来构造拓扑排序。
DFS算法的实现步骤如下:步骤1:初始化一个空的结果列表res和一个记录节点状态的字典visited。
步骤2:对于图中的每个节点v,执行DFS(v):1)若节点v未被访问,则执行步骤3。
2)将节点v标记为已访问。
3)递归地访问v的所有未被访问的邻接节点,并将它们加入结果列表res。
4)将节点v添加到结果列表res的最前面。
步骤3:返回结果列表res的逆序。
三、总结拓扑排序是一种对有向无环图中节点进行排序的算法。
拓扑排序序列的步骤
拓扑排序序列的步骤拓扑排序是一种常用的有向图排序算法,它可以用来解决依赖关系的排序问题。
拓扑排序序列指的是通过拓扑排序算法得到的图中节点的一个线性排序。
在本文中,我们将深入探讨拓扑排序的步骤并给出实现示例。
一、拓扑排序简介拓扑排序适用于有向无环图(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的节点、遍历节点及更新节点的入度,使用队列来实现算法过程。
拓扑排序的原理及其实现
拓扑排序的原理及其实现取材自以下材料:/wiki/Topological_sorting/wiki/Hamiltonian_path定义和前置条件:定义:将有向图中的顶点以线性方式进行排序。
即对于任何连接自顶点u到顶点v的有向边uv,在最后的排序结果中,顶点u总是在顶点v的前面。
如果这个概念还略显抽象的话,那么不妨考虑一个非常非常经典的例子——选课。
我想任何看过数据结构相关书籍的同学都知道它吧。
假设我非常想学习一门“机器学习”的课程,但是在修这么课程之前,我们必须要学习一些基础课程,比如:计算机科学概论,C语言程序设计,数据结构,算法等等。
那么这个制定选修课程顺序的过程,实际上就是一个拓扑排序的过程,每门课程相当于有向图中的一个顶点,而连接顶点之间的有向边就是课程学习的先后关系。
只不过这个过程不是那么复杂,从而很自然的在我们的大脑中完成了。
将这个过程以算法的形式描述出来的结果,就是拓扑排序。
那么是不是所有的有向图都能够被拓扑排序呢?显然不是。
继续考虑上面的例子,如果告诉你在选修“计算机科学概论”这门课之前需要你先学习“机器学习”,你是不是会被弄糊涂?在这种情况下,就无法进行拓扑排序,因为它中间存在互相依赖的关系,从而无法确定谁先谁后。
在有向图中,这种情况被描述为存在环路。
因此,一个有向图能被拓扑排序的充要条件就是它是一个有向无环图(DAG:Directed Acyclic Graph)。
偏序/全序关系:偏序和全序实际上是离散数学中的概念。
这里不打算说太多形式化的定义,形式化的定义教科书上或者上面给的链接中就说的很详细。
还是以上面选课的例子来描述这两个概念。
假设我们在学习完了算法这门课后,可以选修“机器学习”或者“计算机图形学”。
这个“或者”表示,学习“机器学习”和“计算机图形学”这两门课之间没有特定的先后顺序。
因此,在我们所有可以选择的课程中,任意两门课程之间的关系要么是确定的(即拥有先后关系),要么是不确定的(即没有先后关系),绝对不存在互相矛盾的关系(即环路)。
离散数学有向图算法应用实例分析
离散数学有向图算法应用实例分析离散数学是计算机科学中的重要学科之一,它研究的是离散对象和离散结构及其相互关系的数学理论。
有向图是离散数学中的一个重要概念,它由一组节点和一组有方向的边组成,边表示节点间的关系。
在离散数学中,有向图算法是应用非常广泛而强大的工具。
下面我们将通过几个实例来分析离散数学有向图算法的应用。
实例一:拓扑排序拓扑排序是有向图中的一种重要算法,它用于对有向图进行排序。
该算法可以帮助我们找到适合的执行顺序,以满足所有任务的依赖关系。
假设我们有一个项目需要完成,并且任务之间存在一定的依赖关系。
我们可以使用有向图来表示任务,节点表示任务,有向边表示依赖关系。
通过拓扑排序算法,我们可以确定任务的合理执行顺序。
实例二:最短路径算法最短路径算法是有向图应用中的另一个重要领域。
它用于解决从一个节点到另一个节点的最短路径问题。
在许多实际应用中,比如地图导航、网络路由等,最短路径算法都能够提供有效的解决方案。
以地图导航为例,我们可以将道路抽象成有向图,节点表示地点,边表示道路,边的权重表示道路的长度。
通过最短路径算法,我们可以找到从起点到终点的最短路径,并提供有效的导航指引。
实例三:网络流算法网络流算法是有向图算法中的又一重要应用。
它主要用于解决网络中货物、信息等流动的问题。
通过网络流算法,我们可以找到网络中的最大流或最小割,从而优化网络资源的利用。
以货物流动为例,我们可以将供应链抽象成有向图,节点表示供应链中的各个环节,边表示货物流动的路径,边的容量表示货物的承载能力。
通过网络流算法,我们可以确定供应链中的最大流量,并优化流动路径,提高资源的利用效率。
通过以上几个实例,我们可以看到离散数学中的有向图算法在实际应用中的重要性和广泛性。
它们可以帮助我们解决各种问题,并提供有效的解决方案。
因此,对于计算机科学专业的学生来说,深入学习和理解离散数学有向图算法是至关重要的。
总结:离散数学有向图算法是计算机科学中的重要工具之一。
图基本算法拓扑排序(基于dfs)
图基本算法拓扑排序(基于dfs) 拓扑排序,是对有向⽆回路图进⾏排序,以期找到⼀个线性序列,这个线性序列在⽣活正可以表⽰某些事情完成的相应顺序。
如果说所求的图有回路的话,则不可能找到这个序列。
在⼤学数据结构课上,我们知道求拓扑排序的⼀种⽅法。
⾸先⽤⼀个⼊度数组保存每个顶点的⼊度。
在进⾏拓扑排序时,我们需要找到⼊度为0的点,将其存⼊线性序列中,再将其从图中删除(与它相关的边都删除,相邻的顶点的⼊度均减1),再重复上⾯的操作,直⾄所有的顶点都被找到为⽌。
如果不对每次找⼊度为0的顶点的⽅法进⾏处理,⽽直接去遍历⼊度数组,则该算法的时间复杂度为O(|V|2),如果使⽤⼀个队列来保存⼊度为0的顶点,则可以将这个算法的复杂度降为O(V+E)。
今天在算法导论上看了⽤dfs来求拓扑排序的算法,才发现其⾼深之处,膜拜之Orz…下⾯是算法导论的叙述: 本节说明了如何运⽤深度优先搜索,对⼀个有向⽆回路图(dag)进⾏拓扑排序。
对有向⽆回路图G=(V,E)进⾏拓扑排序后,结果为该图顶点的⼀个线性序列,满⾜如果G包含边(u, v),则在该序列中,u就出现在v的前⾯(如果图是有回路的,就不可能存在这样的线性序列)。
⼀个图的拓扑排序可以看成是图中所有顶点沿⽔平线排列⽽成的⼀个序列。
使得所有的有向边均从左指向右。
因此,拓扑排序不同于通常意义上的排序。
在很多应⽤中,有向⽆回路图⽤于说明时间发⽣的先后次序,下图1即给出⼀个实例,说明Bumstead教授早晨穿⾐的过程。
他必须先穿好某些⾐服,才能再穿其他⾐服(如先穿袜⼦后穿鞋),其他⼀些⾐服则可以按任意次序穿戴(如袜⼦和裤⼦),在图1中,有向边<u,v>表⽰⾐服u必须先于⾐服v穿戴。
因此,该图的拓扑排序给出了⼀个穿⾐的顺序。
图2说明了对该图进⾏拓扑排序后,将沿⽔平线⽅向形成⼀个顶点序列,使得图中所有有向边均从左指向右。
拓扑排序算法具体步骤如下:1、调⽤dfs_travel();2、在dfs_travel()每次调⽤dfs()的过程中,都记录了顶点s的完成时间,将顶点s按完成顺序保存在存放拓扑排序顺序的数组topoSort[]中。
数据结构课设——有向图的深度、广度优先遍历及拓扑排序
数据结构课设——有向图的深度、⼴度优先遍历及拓扑排序任务:给定⼀个有向图,实现图的深度优先, ⼴度优先遍历算法,拓扑有序序列,并输出相关结果。
功能要求:输⼊图的基本信息,并建⽴图存储结构(有相应提⽰),输出遍历序列,然后进⾏拓扑排序,并测试该图是否为有向⽆环图,并输出拓扑序列。
按照惯例,先上代码,注释超详细:#include<stdio.h>#include<stdlib.h>#include<malloc.h>#pragma warning(disable:4996)#define Max 20//定义数组元素最⼤个数(顶点最⼤个数)typedef struct node//边表结点{int adjvex;//该边所指向结点对应的下标struct node* next;//该边所指向下⼀个结点的指针}eNode;typedef struct headnode//顶点表结点{int in;//顶点⼊度char vertex;//顶点数据eNode* firstedge;//指向第⼀条边的指针,边表头指针}hNode;typedef struct//邻接表(图){hNode adjlist[Max];//以数组的形式存储int n, e;//顶点数,边数}linkG;//以邻接表的存储结构创建图linkG* creat(linkG* g){int i, k;eNode* s;//边表结点int n1, e1;char ch;g = (linkG*)malloc(sizeof(linkG));//申请结点空间printf("请输⼊顶点数和边数:");scanf("%d%d", &n1, &e1);g->n = n1;g->e = e1;printf("顶点数:%d 边数:%d\n", g->n, g->e);printf("请输⼊顶点信息(字母):");getchar();//因为接下来要输⼊字符串,所以getchar⽤于承接上⼀条命令的结束符for (i = 0; i < n1; i++){scanf("%c", &ch);g->adjlist[i].vertex = ch;//获得该顶点数据g->adjlist[i].firstedge = NULL;//第⼀条边设为空}printf("\n打印顶点下标及顶点数据:\n");for (i = 0; i < g->n; i++)//循环打印顶点下标及顶点数据{printf("顶点下标:%d 顶点数据:%c\n", i, g->adjlist[i].vertex);}getchar();int i1, j1;//相连接的两个顶点序号for (k = 0; k < e1; k++)//建⽴边表{printf("请输⼊对<i,j>(空格分隔):");scanf("%d%d", &i1, &j1);s = (eNode*)malloc(sizeof(eNode));//申请边结点空间s->adjvex = j1;//边所指向结点的位置,下标为j1s->next = g->adjlist[i1].firstedge;//将当前s的指针指向当前顶点上指向的结点g->adjlist[i1].firstedge = s;//将当前顶点的指针指向s}return g;//返回指针g}int visited[Max];//标记是否访问void DFS(linkG* g, int i)//深度优先遍历{eNode* p;printf("%c ", g->adjlist[i].vertex);visited[i] = 1;//将已访问过的顶点visited值改为1p = g->adjlist[i].firstedge;//p指向顶点i的第⼀条边while (p)//p不为NULL时(边存在){if (visited[p->adjvex] != 1)//如果没有被访问DFS(g, p->adjvex);//递归}p = p->next;//p指向下⼀个结点}}void DFSTravel(linkG* g)//遍历⾮连通图{int i;printf("深度优先遍历;\n");//printf("%d\n",g->n);for (i = 0; i < g->n; i++)//初始化为0{visited[i] = 0;}for (i = 0; i < g->n; i++)//对每个顶点做循环{if (!visited[i])//如果没有被访问{DFS(g, i);//调⽤DFS函数}}}void BFS(linkG* g, int i)//⼴度优先遍历{int j;eNode* p;int q[Max], front = 0, rear = 0;//建⽴顺序队列⽤来存储,并初始化printf("%c ", g->adjlist[i].vertex);visited[i] = 1;//将已经访问过的改成1rear = (rear + 1) % Max;//普通顺序队列的话,这⾥是rear++q[rear] = i;//当前顶点(下标)队尾进队while (front != rear)//队列⾮空{front = (front + 1) % Max;//循环队列,顶点出队j = q[front];p = g->adjlist[j].firstedge;//p指向出队顶点j的第⼀条边while (p != NULL){if (visited[p->adjvex] == 0)//如果未被访问{printf("%c ", g->adjlist[p->adjvex].vertex);visited[p->adjvex] = 1;//将该顶点标记数组值改为1rear = (rear + 1) % Max;//循环队列q[rear] = p->adjvex;//该顶点进队}p = p->next;//指向下⼀个结点}}}void BFSTravel(linkG* g)//遍历⾮连通图{int i;printf("⼴度优先遍历:\n");for (i = 0; i < g->n; i++)//初始化为0{visited[i] = 0;}for (i = 0; i < g->n; i++)//对每个顶点做循环{if (!visited[i])//如果没有被访问过{BFS(g, i);//调⽤BFS函数}}}//因为拓扑排序要求⼊度为0,所以需要先求出每个顶点的⼊度void inDegree(linkG* g)//求图顶点⼊度{eNode* p;int i;for (i = 0; i < g->n; i++)//循环将顶点⼊度初始化为0{g->adjlist[i].in = 0;}for (i = 0; i < g->n; i++)//循环每个顶点{p = g->adjlist[i].firstedge;//获取第i个链表第1个边结点指针while (p != NULL)///当p不为空(边存在){g->adjlist[p->adjvex].in++;//该边终点结点⼊度+1p = p->next;//p指向下⼀个边结点}printf("顶点%c的⼊度为:%d\n", g->adjlist[i].vertex, g->adjlist[i].in);}void topo_sort(linkG *g)//拓扑排序{eNode* p;int i, k, gettop;int top = 0;//⽤于栈指针的下标索引int count = 0;//⽤于统计输出顶点的个数int* stack=(int *)malloc(g->n*sizeof(int));//⽤于存储⼊度为0的顶点for (i=0;i<g->n;i++)//第⼀次搜索⼊度为0的顶点{if (g->adjlist[i].in==0){stack[++top] = i;//将⼊度为0的顶点进栈}}while (top!=0)//当栈不为空时{gettop = stack[top--];//出栈,并保存栈顶元素(下标)printf("%c ",g->adjlist[gettop].vertex);count++;//统计顶点//接下来是将邻接点的⼊度减⼀,并判断该点⼊度是否为0p = g->adjlist[gettop].firstedge;//p指向该顶点的第⼀条边的指针while (p)//当p不为空时{k = p->adjvex;//相连接的顶点(下标)g->adjlist[k].in--;//该顶点⼊度减⼀if (g->adjlist[k].in==0){stack[++top] = k;//如果⼊度为0,则进栈}p = p->next;//指向下⼀条边}}if (count<g->n)//如果输出的顶点数少于总顶点数,则表⽰有环{printf("\n有回路!\n");}free(stack);//释放空间}void menu()//菜单{system("cls");//清屏函数printf("************************************************\n");printf("* 1.建⽴图 *\n");printf("* 2.深度优先遍历 *\n");printf("* 3.⼴度优先遍历 *\n");printf("* 4.求出顶点⼊度 *\n");printf("* 5.拓扑排序 *\n");printf("* 6.退出 *\n");printf("************************************************\n");}int main(){linkG* g = NULL;int c;while (1){menu();printf("请选择:");scanf("%d", &c);switch (c){case1:g = creat(g); system("pause");break;case2:DFSTravel(g); system("pause");break;case3:BFSTravel(g); system("pause");break;case4:inDegree(g); system("pause");break;case5:topo_sort(g); system("pause");break;case6:exit(0);break;}}return0;}实验⽤图:运⾏结果:关于深度优先遍历 a.从图中某个顶点v 出发,访问v 。
拓扑排序结果字典序
拓扑排序结果字典序全文共四篇示例,供读者参考第一篇示例:拓扑排序是一种对有向无环图(DAG)进行排序的方法,它可以保证图中的每个顶点在排序结果中出现的位置都是正确的。
拓扑排序的结果并不唯一,即可能存在不同的排列顺序,但这些顺序都满足拓扑排序的要求。
在拓扑排序中,我们通常会使用一种算法来进行排序,这种算法被称为拓扑排序算法。
其中最经典的算法是Kahn算法,其基本思想是通过不断地删除入度为0的顶点,直到所有顶点都被删除。
删除的顶点即为拓扑排序的结果。
在Kahn算法中,我们会使用一个队列来存储入度为0的顶点,并在每次删除一个顶点后更新其他顶点的入度。
拓扑排序的结果可以有多种形式显示,最常见的形式是将顶点按照其排序顺序输出。
但在某些情况下,我们可能会需要将拓扑排序结果按照字典序进行输出,这时我们需要对拓扑排序的结果进行一定的调整。
在进行拓扑排序时,我们可以将排序结果存储在一个列表中,并通过比较列表中的元素大小,从而得到字典序的拓扑排序结果。
这种方法相对简单直接,但需要注意的是,不同的图可能有不同的结果,因此需要在编程时考虑到这一点。
对于拓扑排序结果的字典序,我们可以举一个例子来说明。
假设我们有一个图G,其拓扑排序结果为{A, B, C, D}。
如果我们需要按照字典序输出拓扑排序结果,那么最终的结果可能会是{A, B, D, C}或者{B, A, D, C}等不同的排列。
除了使用队列和比较列表元素大小之外,我们也可以通过递归的方式来实现拓扑排序结果的字典序输出。
在递归过程中,我们可以利用字典序的性质,对拓扑排序结果进行调整,从而输出符合字典序要求的结果。
拓扑排序结果的字典序输出是一个比较有意义的问题,在实际工程中也有一定的应用场景。
通过合理的算法设计和编程实现,我们可以方便地得到符合要求的拓扑排序结果,从而更好地解决问题。
希望本文能够帮助读者更好地理解拓扑排序以及相关概念,进一步拓展对图论的认识。
第二篇示例:拓扑排序是一种用于有向图的排序方法,它可以将图中的节点按照一定的顺序排列。
数据结构与算法课程设计报告---图的算法实现
数据结构与算法课程设计报告课程设计题目:图的算法实现专业班级:信息与计算科学1002班目录摘要 (1)1、引言 (1)2、需求分析 (1)3、概要设计 (2)4、详细设计 (4)5、程序设计 (10)6、运行结果 (18)7、总结体会 (19)摘要(题目): 图的算法实现实验内容图的算法实现问题描述:(1)将图的信息建立文件;(2)从文件读入图的信息,建立邻接矩阵和邻接表;(3)实现Prim、Kruskal、Dijkstra和拓扑排序算法。
关键字:邻接矩阵、Dijkstra和拓扑排序算法1.引言本次数据结构课程设计共完成图的存储结构的建立、Prim、Kruskal、Dijkstra 和拓扑排序算法等问题。
通过本次课程设计,可以巩固和加深对数据结构的理解,通过上机和程序调试,加深对课本知识的理解和熟练实践操作。
(1)通过本课程的学习,能够熟练掌握数据结构中图的几种基本操作;(2)能针对给定题目,选择相应的数据结构,分析并设计算法,进而给出问题的正确求解过程并编写代码实现。
使用语言:CPrim算法思想:从连通网N={V,E}中的某一顶点v0出发,选择与它关联的具有最小权值的边(v0,v),将其顶点加入到生成树的顶点集合V中。
以后每一步从一个顶点在V中,而另一个顶点不在V中的各条边中选择权值最小的边(u,v),把它的顶点加入到集合V中。
如此继续下去,直到网中的所有顶点都加入到生成树顶点集合V中为止。
拓扑排序算法思想:1、从有向图中选取一个没有前驱的顶点,并输出之;2、从有向图中删去此顶点以及所有以它为尾的弧;重复上述两步,直至图空,或者图不空但找不到无前驱的顶点为止。
没有前驱-- 入度为零,删除顶点及以它为尾的弧-- 弧头顶点的入度减1。
2.需求分析1、通过键盘输入建立一个新的有向带权图,建立相应的文件;2、对建立的有向带权图进行处理,要求具有如下功能:(1)用邻接矩阵和邻接表的存储结构输出该有向带权图,并生成相应的输出结果;(2)用Prim、Kruskal算法实现对图的最小生成树的求解,并输出相应的输出结果;(3)用Dijkstra算法实现对图中从某个源点到其余各顶点的最短路径的求解,并输出相应的输出结果;(4)实现该图的拓扑排序算法。
详解C++实现拓扑排序算法
详解C++实现拓扑排序算法⽬录⼀、拓扑排序的介绍⼆、拓扑排序的实现步骤三、拓扑排序⽰例⼿动实现四、拓扑排序的代码实现五、完整的代码和输出展⽰⼀、拓扑排序的介绍拓扑排序对应施⼯的流程图具有特别重要的作⽤,它可以决定哪些⼦⼯程必须要先执⾏,哪些⼦⼯程要在某些⼯程执⾏后才可以执⾏。
为了形象地反映出整个⼯程中各个⼦⼯程(活动)之间的先后关系,可⽤⼀个有向图来表⽰,图中的顶点代表活动(⼦⼯程),图中的有向边代表活动的先后关系,即有向边的起点的活动是终点活动的前序活动,只有当起点活动完成之后,其终点活动才能进⾏。
通常,我们把这种顶点表⽰活动、边表⽰活动间先后关系的有向图称做顶点活动⽹(Activity On Vertex network),简称AOV⽹。
⼀个AOV⽹应该是⼀个有向⽆环图,即不应该带有回路,因为若带有回路,则回路上的所有活动都⽆法进⾏(对于数据流来说就是死循环)。
在AOV⽹中,若不存在回路,则所有活动可排列成⼀个线性序列,使得每个活动的所有前驱活动都排在该活动的前⾯,我们把此序列叫做拓扑序列(Topological order),由AOV⽹构造拓扑序列的过程叫做拓扑排序(Topological sort)。
AOV⽹的拓扑序列不是唯⼀的,满⾜上述定义的任⼀线性序列都称作它的拓扑序列。
⼆、拓扑排序的实现步骤1.在有向图中选⼀个没有前驱的顶点并且输出2.从图中删除该顶点和所有以它为尾的弧(⽩话就是:删除所有和它有关的边)3.重复上述两步,直⾄所有顶点输出,或者当前图中不存在⽆前驱的顶点为⽌,后者代表我们的有向图是有环的,因此,也可以通过拓扑排序来判断⼀个图是否有环。
三、拓扑排序⽰例⼿动实现如果我们有如下的⼀个有向⽆环图,我们需要对这个图的顶点进⾏拓扑排序,过程如下:⾸先,我们发现V6和v1是没有前驱的,所以我们就随机选去⼀个输出,我们先输出V6,删除和V6有关的边,得到如下图结果:然后,我们继续寻找没有前驱的顶点,发现V1没有前驱,所以输出V1,删除和V1有关的边,得到下图的结果:然后,我们⼜发现V4和V3都是没有前驱的,那么我们就随机选取⼀个顶点输出(具体看你实现的算法和图存储结构),我们输出V4,得到如下图结果:然后,我们输出没有前驱的顶点V3,得到如下结果:然后,我们分别输出V5和V2,最后全部顶点输出完成,该图的⼀个拓扑序列为:v6–>v1—->v4—>v3—>v5—>v2四、拓扑排序的代码实现下⾯,我们将⽤两种⽅法来实现我么的拓扑排序:1.Kahn算法2.基于DFS的拓扑排序算法⾸先我们先介绍第⼀个算法的思路:Kahn的算法的思路其实就是我们之前那个⼿动展⽰的拓扑排序的实现,我们先使⽤⼀个栈保存⼊度为0 的顶点,然后输出栈顶元素并且将和栈顶元素有关的边删除,减少和栈顶元素有关的顶点的⼊度数量并且把⼊度减少到0的顶点也⼊栈。
拓扑排序及关键路径
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. 什么是拓扑排序?拓扑排序是对有向无环图(DAG)进行排序的一种算法。
在有向图中,如果存在一条从节点A到节点B的有向边,那么节点A就必须在节点B之前进行排序。
拓扑排序通过将图中的节点按照一定的顺序进行排列,使得任意两个节点之间不存在环。
拓扑排序可以应用于许多问题,比如任务调度、依赖关系分析等。
2. 拓扑排序算法2.1. 算法原理拓扑排序算法基于深度优先搜索(DFS)或广度优先搜索(BFS)实现。
其基本思想是通过遍历图中的所有节点,并记录每个节点的入度(即指向该节点的边数)。
然后从入度为0的节点开始遍历,并将其加入结果列表中。
然后将与该节点相邻的节点的入度减1,并将新入度为0的节点加入结果列表。
重复此过程直到所有节点都被加入结果列表。
2.2. 算法步骤以下是拓扑排序算法的详细步骤:1.初始化一个空结果列表result和一个空队列queue。
2.遍历图中所有节点,并统计每个节点的入度。
3.将入度为0的节点加入队列queue。
4.当队列queue不为空时,执行以下步骤:–从队列queue中取出一个节点node,并将其加入结果列表result。
–遍历与节点node相邻的所有节点,并将它们的入度减1。
–如果某个节点的入度减为0,则将其加入队列queue。
5.如果结果列表result的长度等于图中的节点数,则说明拓扑排序成功;否则,说明图中存在环。
以下是使用Python语言实现拓扑排序算法的示例代码:from collections import defaultdict, dequedef topological_sort(graph):# 统计每个节点的入度in_degree = defaultdict(int)for node in graph:for neighbor in graph[node]:in_degree[neighbor] += 1# 初始化结果列表和队列result = []queue = deque()# 将入度为0的节点加入队列for node in graph:if in_degree[node] == 0:queue.append(node)# 拓扑排序while queue:node = queue.popleft()result.append(node)for neighbor in graph[node]:in_degree[neighbor] -= 1if in_degree[neighbor] == 0:queue.append(neighbor)if len(result) == len(graph):return resultelse:return None3. 拓扑排序求最长路径在有向无环图中,求最长路径可以通过拓扑排序算法来实现。
有向图的拓扑序列
有向图的拓扑序列本文讲述了有向图的拓扑序列,旨在揭示其关键概念、定义和相关算法,从而探究其在复杂系统中的重要作用。
在图论中,有向图的拓扑序列是一个有序的顶点列表,该序列可以从有向图的顶点出发,按照一定顺序访问并遍历所有顶点,以使所有节点都被访问且不重复访问。
拓扑排序可以用来解决复杂问题,例如表示任务依赖性、多层循环套教程、绘制电路板设计等。
本文将介绍有向图的拓扑序列的定义及意义,以及拓扑排序常见的算法,例如深度优先搜索(DFS)、Khan算法等。
#### 1. 有向图的拓扑序列定义有向图中,拓扑序列是指一个有序的顶点列表,其中,如果有任何一对顶点u和v,u排在v之前,即在拓扑序列中表示u-v之前,则从图中这条有向边表示为头u,尾v。
也就是说,在拓扑序列中,任何相邻的顶点之间都没有有向边,换而言之,任何顶点都不能指向先于它的顶点。
拓扑序列的典型应用有:编译器的编译排序、任务调度、布线层次的选择性去除,关于任务的依赖性,以及其他有向图的属性探求。
#### 2. 拓扑排序算法拓扑排序算法是构建拓扑序列的基础,常见的拓扑排序包括深度优先搜索(DFS)和Kahn算法。
(1)深度优先搜索算法(DFS)深度优先搜索算法是一种使用回溯法来构造拓扑排序的算法,它以深度优先的方式搜索有向图中的顶点,然后向着没有被访问过的邻接顶点转移。
算法结束时,拓扑序列是由深度优先搜索序列得到的,且节点顺序相反。
(2)Kahn算法Kahn算法也是一种构造拓扑序列的算法,将有向图的节点看成一个队列,在循环中,始终取出队首的节点,如果该节点不指向任何其它节点,则加入拓扑序列;如果该节点指向其它节点,则将这些被指向的节点从队列中删除,当循环结束时,如果队列为空,则得到一个拓扑序列,如果循环结束后,队列中仍有节点,则表明有向图存在环,不可以构成拓扑序列。
#### 3. 结论有向图的拓扑序列是图论中的一个术语,其构造拓扑序列的算法包括深度优先搜索(DFS)和Kahn算法,拓扑序列可以用来解决复杂问题,如任务依赖性、多层循环套教程、绘制电路板设计等。
数据结构-拓扑排序介绍
成绩14信计 2015-2016(一)数据结构课程设计设计题目拓扑排序设计时间——学生姓名冯佳君学生学号20140401105所在班级14信计 1指导教师刘风华徐州工程学院数学与物理科学学院一、需求剖析1.问题描绘本次课程设计题目是:用毗邻表结构图而后进行拓扑排序,输出拓扑排序序列。
拓扑排序的基本思想为:1)从有向图中选一个无前驱的极点输出;2)将此极点和以它为起点的弧删除;3)重复 1) 、 2) 直到不存在无前驱的极点;4)若此时输出的极点数小于有向图中的极点数,则说明有向图中存在回路,不然输出的极点的次序即为一个拓扑序列。
2.拓扑排序有向图拓朴排序算法的基本步骤以下:1 )从图中选择一个入度为0 的极点,输出该极点;2 )从图中删除该极点及其有关系的弧,调整被删弧的弧头结点的入度(入度-1 );3)重复履行 1)、 2)直到全部极点均被输出,拓朴排序达成或许图中再也没有入度为 0 的极点(此种状况说明原有向图含有环)。
3.基本要求(1)输入的形式和输入值的范围;第一是输入要排序的极点数和弧数,都为整型,中间用分开符分开;再输入各极点的值,为正型,中间用分开符分开;而后输入各条弧的两个极点值,先输入弧头,再输入弧尾,中间用分开符分开,输入的值只好是开始输入的极点值不然系统会提示输入的值的极点值不正确,请从头输入,只需持续输入正确的值就行。
(2)输出的形式;第一输出成立的毗邻表,而后是最后各极点的出度数,再是拓扑排序的序列,而且每输出一个极点,就会输出一次各极点的入度数。
(3)程序所能达到的功能;因为该程序是求拓扑排序,所以算法的功能就是要输出拓扑排序的序列,在一个有向图中,若用极点表示活动,有向边就表示活动间先后次序,那么输出的拓扑序列就表示各极点间的关系为反应出各点的储存结构,以毗邻表储存并输出各极点的入度。
二、纲要设计1.算法顶用到的全部各样数据种类的定义在该程序顶用毗邻表作为图的储存结构。
图算法(2) --拓扑排序,2-SAT,欧拉路 - TOJ
• Catenyms (Waterloo Jan 03)
– TOJ 1416 / POJ 2337 – 题目大意:给定一组单词,问这些单词能否 连成一串,使得前面一个单词的最后一个字 母和后面一个单词的第一个字母相同。
1
24
例题分析
0011 0010 1010 1101 0001 0100 1011 • Sample Input • Sample
1
• 检验此SAT问题是否有解,即可验证S是否可以
17
Horn-SAT
0011 0010 1010 1101 0001 0100 1011
• 每个子句里最多有一个positive literal
(¬A ∨ ¬B ∨ C ) ∧ ( B ∨ ¬D) ∧L
• 存在多项式的构造算法:
– 如果每个子句都包含大于1个变量,则所有变量 取负即为解 – 否则,存在包含1个变量的子句,则这些变量的 值可唯一确定,代入原式化简即可
– (1)编号为1的点排得越靠前越好 – (2)在满足(1)的所有序列中,2越靠前越好 – (3)…3越靠前越好 –…
1
6
例题分析
0011 0010 1010 1101 0001 0100 1011
• 仍按字典序可以么?
4 2 3 1
倒序的字典序最 大即为所求……
1
• 1应该尽可能的早……在1最早的前提下2 尽可能早…………
1
30
例题分析
0011 0010 1010 1101 0001 0100 1011
• 当k=3, b=2时,一个合法的环为:
00010111
• 数学问题?? 没有好的办法 • 暴力搜索?? 复杂度为O(b^(b^k)),无 法接受
【图论】有向无环图的拓扑排序
【图论】有向⽆环图的拓扑排序1. 引⾔有向⽆环图(Directed Acyclic Graph, DAG)是有向图的⼀种,字⾯意思的理解就是图中没有环。
常常被⽤来表⽰事件之间的驱动依赖关系,管理任务之间的调度。
拓扑排序是对DAG的顶点进⾏排序,使得对每⼀条有向边(u, v),均有u(在排序记录中)⽐v先出现。
亦可理解为对某点v⽽⾔,只有当v的所有源点均出现了,v才能出现。
下图给出有向⽆环图的拓扑排序:下图给出的顶点排序不是拓扑排序,因为顶点D的邻接点E⽐其先出现:2. 算法原理与实现拓扑排序的实现算法有两种:⼊度表、DFS,其时间复杂度均为O(V+E)。
⼊度表对于DAG的拓扑排序,显⽽易见的办法:找出图中0⼊度的顶点;依次在图中删除这些顶点,删除后再找出0⼊度的顶点;然后再删除……再找出……直⾄删除所有顶点,即完成拓扑排序为了保存0⼊度的顶点,我们采⽤数据结构栈(亦可⽤队列);算法的可视化可参看。
图⽤邻接表(adjacency list)表⽰,⽤数组inDegreeArray[]记录结点的⼊度变化情况。
C实现:// get in-degree arrayint *getInDegree(Graph *g) {int *inDegreeArray = (int *) malloc(g->V * sizeof(int));memset(inDegreeArray, 0, g->V * sizeof(int));int i;AdjListNode *pCrawl;for(i = 0; i < g->V; i++) {pCrawl = g->array[i].head;while(pCrawl) {inDegreeArray[pCrawl->dest]++;pCrawl = pCrawl->next;}}return inDegreeArray;}// topological sort functionvoid topologicalSort(Graph *g) {int *inDegreeArray = getInDegree(g);Stack *zeroInDegree = initStack();int i;for(i = 0; i < g->V; i++) {if(inDegreeArray[i] == 0)push(i, zeroInDegree);}printf("topological sorted order\n");AdjListNode *pCrawl;while(!isEmpty(zeroInDegree)) {i = pop(zeroInDegree);printf("vertex %d\n", i);pCrawl = g->array[i].head;while(pCrawl) {inDegreeArray[pCrawl->dest]--;if(inDegreeArray[pCrawl->dest] == 0)push(pCrawl->dest, zeroInDegree);pCrawl = pCrawl->next;}}}时间复杂度:得到inDegreeArray[]数组的复杂度为O(V+E);顶点进栈出栈,其复杂度为O(V);删除顶点后将邻接点的⼊度减1,其复杂度为O(E);整个算法的复杂度为O(V+E)。
图的基本概念及拓扑排序
有n-1条边。 如果在生成树上添加1条边,必定构成一个环。 若图中有n个顶点,却少于n-1条边,必为非连通 图。
最小生成树:若无向连通带权图G=<V,E,W>,T是G的一棵生成树,T的各边权之
和称为T的权,记做W(T),G的所有生成树中权值最小的生成树 称为最小生成树。
带权图: 即边上带权的图。其中权是指每条边可以标上 具有某种含义的数值(即与边相关的数)。
网 络: =带权图
路径: 在图 G=(V, E) 中, 若从顶点 vi 出发, 沿一些边经过一
些顶点 vp1, vp2, …, vpm,到达顶点vj。则称顶点序列 ( vi vp1 vp2 ... vpm vj ) 为从顶点vi 到顶点 vj 的路径。它经过的边(vi, vp1)、(vp1, vp2)、...、(vpm, vj)应当是属于E的边。
最小生成树算法: Prim算法和kruskal算法
简单路径:路径上各顶点 v1,v2,...,vm 均不互相重复。
回 路: 若路径上第一个顶点 v1 与最后一个顶点vm 重合,
则称这样的路径为回路或环。
例:
图的数学表示
点: 用整数0, 1, 2, …, V-1表示 边: 用无序数对(u, v)表示, 或者表示成u-v
4. 你认为,对于给定的两个位置A,B,聪明的机器人从A位置到B位置至少需要判断几次?
5. input
6. 第一行:M 表示以下有M组测试数据(0<M<=8)
7. 接下来每组有两行数据
8.
头一行:N A B(1<=N<=50,1<=A,B<=N)
9.
下一行:K1 K2···Kn(0<=Ki<=N)
数据结构课程设计——拓扑排序
. . .. . .课程设计任务书学生:专业班级:指导教师:工作单位:计算机科学系题目: 拓扑排序初始条件:(1)采用邻接表作为有向图的存储结构;(2)给出所有可能的拓扑序列。
(3)测试用例见严蔚敏《数据结构习题集(C语言版)》p48题7.9图要求完成的主要任务:(包括课程设计工作量及其技术要求,以及说明书撰写等具体要求)课程设计报告按学校规定格式用A4纸打印(书写),并应包含如下容:1. 问题描述简述题目要解决的问题是什么。
2. 设计存储结构设计、主要算法设计(用类C/C++语言或用框图描述)、测试用例设计;3. 调试报告调试过程中遇到的问题是如何解决的;对设计和编码的讨论和分析。
4. 经验和体会(包括对算法改进的设想)5. 附源程序清单和运行结果。
源程序要加注释。
如果题目规定了测试数据,则运行结果要包含这些测试数据和运行输出。
说明:1. 设计报告、程序不得相互抄袭和拷贝;若有雷同,则所有雷同者成绩均为0分。
2. 凡拷贝往年任务书或课程设计充数者,成绩一律无效,以0分记。
时间安排:1.第17周完成,验收时间由指导教师指定2.验收地点:实验中心3.验收容:可执行程序与源代码、课程设计报告书。
指导教师签名:2013年6月14日系主任(或责任教师)签名:年月日拓扑排序目录1问题描述2具体设计2.1存储结构设计2.2主要算法设计2.2.1拓扑排序的算法总体设计2.2.2将有向图表示为邻接表2.2.3拓扑排序函数的设计2.2.4顺序表的运算设计2.3测试用例设计3调试报告3.1设计和编码的分析3.2调试过程问题及解决4经验与体会5用户使用说明6参考文献7附录源代码与运行结果1问题描述题目:拓扑排序如果用有向图表示一个工程,在这种有向图中,用顶点表示活动,用有向边<vi,vj>表示活动vi必须先于活动vj进行,这种有向图叫做顶点表示活动的网络,记作AOV 网络。
对一个有向无环图G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得AOV网络中的所有应存在前驱和后继的关系都能得到满足,这种构造AOV网络全部顶点的拓扑有序序列的运算叫做拓扑排序。
数据结构中拓扑排序算法的实现
Ac hi e v e o f To po l o g i c a l Ta x i s Ca l c ul a t i o n i n Da t a St r uc t ur e
LI J i n g
( Gu i z h o u Co mme r c i a l Co U e g e , De p a r t me n t o f Co mp u t e r S c i e n c e a n d T e c h n o l o g y, Gu i y ng a 5 5 0 0 0 4, Ch i n a )
电脑 编程技巧 与维 护 源自数 据结构 中拓 扑排序算 法 的实现
奎 静
( 贵州商业高等专科学校计算机科学与技术系 ,贵阳 5 5 o o o 4 ) 摘 要 :介绍拓扑排序 的算 法,对 于给 出的事件 结点 网络 ,要求依 次求 出入度 ( 或 出度)为 0的顶 点 ,最终得 到一
组拓 扑序 列。通过 对这 一序 列的分析 、比较 ,判 断该 网络 图是否 为循环 图 ,从 而确立 实际应 用的可能性 大小 ,并给 出了计算机 上机 实现 的源程序 。 关键 词 :拓 扑序 列 ; A O E网 ; 数据结构
又如 ,汽车装 配工 程可分解为 以下任 务 :将底盘 放上装配线 ,
装轴 ,将 座位 装在底 盘上 、上 漆 、装 刹车 、装 门等 。任 务之 间具有先后关 系 ,例如在装轴之前必 须先将底板 放上装配线 。
要来 描 述 这 些任 务 的先 后 顺序 就可 用 A O V网 ( A c t i v i t y O n
这些 事件 都可分 为若干个 子事件 ,只要 依次完 成它 们 ,就 可 以最终促 成整个事 件 的完 成 。但这 些子 事件之 间有 时存 在一 定的先决 条件 约束 ,即有 些子事 件必 须在其他 子事 件完 成 以 后方可 开始实施 ;而有些 子事件 则没有 这样 的约束 关 系。例 如 :将早 晨穿 衣的过程看 作是一 项小 工程 。那 必须 先穿 某一 衣物才 能再穿 其他衣物 ( 如先穿 袜子后 穿鞋) ,逐 步完成 工程 的运作 ,也有一些 衣物可以按任意次序穿戴 ( 如袜子 和短裤) 。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
数据结构课程设计设计说明书有向图拓扑排序算法的实现学生姓名学号班级成绩指导教师魏佳计算机科学与技术系2010年2月22日数据结构课程设计评阅书注:指导教师成绩60%,答辩成绩40%,总成绩合成后按五级制记入。
课程设计任务书2010—2011学年第二学期专业:信息管理与信息系统学号:姓名:课程设计名称:数据结构课程设计设计题目:有向图拓扑排序算法的实现完成期限:自2011 年 2 月22 日至2011 年 3 月 4 日共 2 周设计内容:用C/C++编写一个程序实现有向图的建立和排序。
要求建立有向图的存储结构,从键盘输入一个有向图,程序能够自动进行拓扑排序。
设计要求:1)问题分析和任务定义:根据设计题目的要求,充分地分析和理解问题,明确问题要求做什么?(而不是怎么做?)限制条件是什么?确定问题的输入数据集合。
2)逻辑设计:对问题描述中涉及的操作对象定义相应的数据类型,并按照以数据结构为中心的原则划分模块,定义主程序模块和各抽象数据类型。
逻辑设计的结果应写出每个抽象数据类型的定义(包括数据结构的描述和每个基本操作的功能说明),各个主要模块的算法,并画出模块之间的调用关系图;3)详细设计:定义相应的存储结构并写出各函数的伪码算法。
在这个过程中,要综合考虑系统功能,使得系统结构清晰、合理、简单和易于调试,抽象数据类型的实现尽可能做到数据封装,基本操作的规格说明尽可能明确具体。
详细设计的结果是对数据结构和基本操作做出进一步的求精,写出数据存储结构的类型定义,写出函数形式的算法框架;4)程序编码:把详细设计的结果进一步求精为程序设计语言程序。
同时加入一些注解和断言,使程序中逻辑概念清楚;5)程序调试与测试:采用自底向上,分模块进行,即先调试低层函数。
能够熟练掌握调试工具的各种功能,设计测试数据确定疑点,通过修改程序来证实它或绕过它。
调试正确后,认真整理源程序及其注释,形成格式和风格良好的源程序清单和结果;6)结果分析:程序运行结果包括正确的输入及其输出结果和含有错误的输入及其输出结果。
算法的时间、空间复杂性分析;7)编写课程设计报告;以上要求中前三个阶段的任务完成后,先将设计说明数的草稿交指导老师面审,审查合格后方可进入后续阶段的工作。
设计工作结束后,经指导老师验收合格后将设计说明书打印装订,并进行答辩。
指导教师(签字):教研室主任(签字):批准日期:2011年2月21 日摘要设计了一个对有向图进行拓扑排序的算法,该算法首先用邻接表构造有向图的存储结构,然后对此有向图进行拓扑排序,输出拓扑排序的结果。
本算法采用VC++作为软件开发环境,以邻接表作为图的存储结构,将图中所有顶点排成一个线性序列,输出拓扑排序结果。
该算法操作简单,易于用户操作接受。
关键词:数据结构;有向图;拓扑排序目录1 课题描述 (1)2 问题分析和任务定义 (2)3 逻辑设计 (3)3.1程序模块功能图 (3)3.2 抽象数据类型 (3)4 详细设计 (4)4.1 C语言定义的相关数据类型 (4)4.2 主要模块的伪码算法 (4)4.2.1主函数伪码算法: (4)4.2.2邻接表伪码算法: (4)4.2.3拓扑排序的伪码算法: (5)4.3 主函数流程图 (6)5 程序编码 (7)6 程序调试与测试 (13)7 结果分析 (16)8 总结 (17)参考文献 (18)1 课题描述根根据设计要求运用c语言程序设计了一个对有向图进行拓扑排序的算法,该算法首先用邻接表构造有向图的存储结构,然后对此有向图进行拓扑排序,输出拓扑排序的结果。
如给定一个有向无环图如图1.1所示。
在此图中,从入度为0的顶点出发,删除此顶点和所有以它为尾的弧;重复直至全部顶点均已输出;或者当图中不存在无前驱的顶点为止。
图1.1 有向无环图开发工具:visual c++6.0。
2 问题分析和任务定义对一个有向无环图G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对由某个集合上的一个偏序得到该集合上的一个全序,这个操作就称之为拓扑排序。
偏序集合中仅有部分成员之间颗比较,而全序指集合中全体成员之间均可比较,而由偏序定义得到拓扑有序的操作便是拓扑排序。
一个表示偏序的有向图可用来表示一个流程图,通过抽象出来就是AOV-网,若从顶点i到顶点j有一条有向路径,则i是j的前驱,j是i的后继。
若(i,j)是一条弧,则i 是j的直接前驱;j是i的直接后继。
在AOV-网中,不应该出现有向环,用拓扑排序就可以判断网中是否有环,若网中所有顶点都在它的拓扑有序序列中,则该AOV-网必定不存在环。
3.1程序模块功能图图3.1程序模块功能图3.2 抽象数据类型ADT ALGraph{数据对象:D={V|V是具有相同特性的数据元素的集合,即顶点集} 数据关系:R={<v,w>|v,w∈V,<v,w>表示顶点v到顶点w的弧}基本操作P:CreatGraphlist(ALGraph *G)初始条件:成对输入顶点集V中的点。
操作结果:构造图G的邻接表。
FindInDegree(ALGraph G, int indegree[])初始条件:图G的邻接表中存在结点V。
操作结果:找到图中入度为0结点。
Initgraph()操作结果:完成图形初始化。
TopologicalSort(ALGraph G)初始条件:构造的有向图G已初始化。
操作结果:对于有向图G根据邻接存储表进行拓扑排序。
}4.1 C语言定义的相关数据类型#define max_vextex_num 20 /*宏定义最大顶点个数*/#define stack_init_size 100 /*宏定义栈的存储空间大小*/typedef int ElemType;typedef struct VNode /*邻接表头结点的类型*/{int data; /*顶点信息,数据域*/}VNode, AdjList[MAX_VEXTEX_NUM]; /*AdjList是邻接表类型*/typedef struct{AdjList vertices; /*邻接表*/int vexnum, arcnum; /*图中顶点数vexn和边数arcn*/}ALGraph; /*图的类型*/typedef struct //构建栈{ElemType *base; /*数据域*/ElemType *top; /*栈指针域*/int stacksize;}SqStack;4.2 主要模块的伪码算法4.2.1主函数伪码算法:开始{创建及输出邻接表CreatGraphlist(&G);输出排序后的输出序列TopologicalSort(G);}结束4.2.2邻接表伪码算法:#define MAX_VEXTEX_NUM 20typedef struct VNode /*邻接表头结点的类型*/{int data; /*顶点信息,数据域*/ArcNode *firstarc; /*指向第一条弧*/}VNode, AdjList[MAX_VEXTEX_NUM]; /*AdjList是邻接表类型*/typedef struct{AdjList vertices; /*邻接表*/int vexnum, arcnum; /*图中顶点数vexn和边数arcn*/}ALGraph; /*图的类型*/开始{定义一个指针P置i的初值为1邻接表中所有头结点指针置初值当i<=G-vexnum时自加,执行下面操作:输出数据域里的顶点信息使指针p指向顶点i第一条弧的头结点输出访问顶点使指针p指向顶点i的下一条弧的头结点类此循环到输出最后一个顶点}结束4.2.3拓扑排序的伪码算法:开始{引入栈操作函数和入度操作函数访问邻接存储表中的顶点nIf该顶点入度为0顶点进栈循环操作到所有顶点入栈当栈不为空顶点出栈}结束4.3 主函数流程图主函数流程图如图4.3所示:图4.3 主函数程序流程图5 程序编码#include<stdio.h>#include<stdlib.h>#define true 1#define false 0#define MAX_VEXTEX_NUM 20#define M 20#define STACK_INIT_SIZE 100#define STACKINCREMENT 10/*-----------------------图的邻接表存储结构------------------------*/typedef struct ArcNode /*弧结点结构类型*/{int adjvex; /*该弧指向的顶点的位置*/struct ArcNode *nextarc; /*指向下一条弧的指针*/}ArcNode;typedef struct VNode /*邻接表头结点类型*/{int data; /*顶点信息*/ArcNode *firstarc; /*指向第一条依附于该点的弧的指针*/}VNode,AdjList[MAX_VEXTEX_NUM]; /*AdjList为邻接表类型*/typedef struct{AdjList vertices;int vexnum, arcnum;}ALGraph;/*----------------------------------------------------------------*/void CreatGraph(ALGraph *G) /*通过用户交互产生一个图的邻接表*/{int m, n, i;ArcNode *p;printf("=======================================================");printf("\n输入顶点数:");scanf("%d",&G->vexnum);printf("\n输入边数:");scanf("%d",&G->arcnum);printf("=======================================================");for (i=1; i<=G->vexnum;i++) /*初始化各顶点*/{G->vertices[i].data=i; /*编写顶点的位置序号*/G->vertices[i].firstarc=NULL;for (i=1;i<=G->arcnum;i++) /*记录图中由两点确定的弧*/{printf("\n输入确定弧的两个顶点u,v:");scanf("%d %d",&n,&m);while (n<0||n>G->vexnum||m<0||m>G->vexnum){printf("输入的顶点序号不正确请重新输入:");scanf("%d%d",&n,&m);}p=(ArcNode*)malloc(sizeof(ArcNode)); /*开辟新的弧结点来存储用户输入的弧信息*/if(p==NULL){printf("ERROR!");exit(1);}p->adjvex=m; /*该弧指向位置编号为m的结点*/p->nextarc=G->vertices[n].firstarc;/*下一条弧指向的是依附于n的第一条弧*/G->vertices[n].firstarc=p;}printf("=======================================================");printf("\n建立的邻接表为:\n");/*打印生成的邻接表(以一定的格式)*/for(i=1;i<=G->vexnum;i++){printf("%d",G->vertices[i].data);for(p=G->vertices[i].firstarc;p;p=p->nextarc)printf("-->%d",p->adjvex);printf("\n");}printf("======================================================="); }/*----------------------------------------------------------------*/typedef struct /*栈的存储结构*/{int *base; /*栈底指针*/int *top; /*栈顶指针*/int stacksize;/*----------------------------------------------------------------*/void InitStack(SqStack *S) /*初始化栈*/{S->base=(int *)malloc(STACK_INIT_SIZE*sizeof(int));if(!S->base) /*存储分配失败*/{printf("ERROR!");exit(1);}S->top=S->base;S->stacksize=STACK_INIT_SIZE;}/*----------------------------------------------------------------*/void Push(SqStack *S,int e) /*压入新的元素为栈顶*/{if(S->top-S->base>=S->stacksize){S->base=(int *)realloc(S->base,(S->stacksize+STACKINCREMENT)*sizeof(int)); /*追加新空间*/if(!S->base) /*存储分配失败*/{printf("ERROR!");exit(1);}S->top=S->base+S->stacksize;S->stacksize+=STACKINCREMENT;}*S->top++=e; /*e作为新的栈顶元素*/}/*----------------------------------------------------------------*/int Pop(SqStack *S,int *e) /*弹出栈顶,用e返回*/{if(S->top==S->base) /*栈为空*/{return false;}*e=*--S->top;return 0;}/*----------------------------------------------------------------*/int StackEmpty(SqStack *S) /*判断栈是否为空,为空返回1,不为空返回0*/ {if(S->top==S->base)return true;elsereturn false;}/*----------------------------------------------------------------*/void FindInDegree(ALGraph G, int indegree[]) /*对各顶点求入度*/{int i;for(i=1; i<=G.vexnum;i++) /*入度赋初值0*/{indegree[i]=0;}for(i=1;i<=G.vexnum;i++){while(G.vertices[i].firstarc){indegree[G.vertices[i].firstarc->adjvex]++;/*出度不为零,则该顶点firstarc域指向的弧指向的顶点入度加一*/G.vertices[i].firstarc = G.vertices[i].firstarc->nextarc;}}}/*----------------------------------------------------------------*/void TopoSort(ALGraph G){int indegree[M];int i, k, n;int count=0; /*初始化输出计数器*/ArcNode *p;SqStack S;FindInDegree(G,indegree);InitStack(&S);for(i=1;i<=G.vexnum;i++){printf("\n");printf("indegree[%d] = %d \n",i,indegree[i]); /*输出入度*/}printf("\n");for(i=1;i<=G.vexnum;i++) /*入度为0的入栈*/{if(!indegree[i])Push(&S,i);}printf("=======================================================");printf("\n\n拓扑排序序列为:");while(!StackEmpty(&S)) /*栈不为空*/{Pop(&S,&n); /*弹出栈顶*/printf("%4d",G.vertices[n].data); /*输出栈顶并计数*/count++;for(p=G.vertices[n].firstarc; p!=NULL;p=p->nextarc)/*n号顶点的每个邻接点入度减一*/{k=p->adjvex;if(!(--indegree[k])) /*若入度减为零,则再入栈*/{Push(&S,k);}}}if(count<G.vexnum)/*输出顶点数小于原始图的顶点数,有向图中有回路*/{printf("ERROR 出现错误!");}else{printf(" 排序成功!");}}/*----------------------------------------------------------------*/main(void) /*编写主调函数以调用上述被调函数*/{ALGraph G;CreatGraph(&G); /*建立邻接表*/TopoSort(G); /*对图G进行拓扑排序*/printf("\n\n");system("pause");/*调用系统的dos命令:pause;显示:"按任意键继续..."*/ return 0;}6 程序调试与测试(1) 当为有向无环图结构如图6.1所示:图6.1 有向无环图输出结果如图6.2为:图6.2 有向无环图的输出结果(2)当为有向有环图结构如图6.3所示:图6.3 有向有环图结构输出结果如图6.4所示:(3)输入检验图如图6.5所示:由邻接表定义可以得到上图的邻接表如图6.6所示:图6.6邻接表其中一种拓扑序列: 2 7 1 3 4 6 5将图输入到程序中结果如图6.7所示:图6.8 检验图的输出所得结果与预计结果一致。