有向图拓扑排序算法的实现

合集下载

拓扑排序了解拓扑排序的概念和实现方式

拓扑排序了解拓扑排序的概念和实现方式

拓扑排序了解拓扑排序的概念和实现方式拓扑排序:了解拓扑排序的概念和实现方式拓扑排序是一种用于有向无环图(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)

图基本算法拓扑排序(基于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}等不同的排列。

除了使用队列和比较列表元素大小之外,我们也可以通过递归的方式来实现拓扑排序结果的字典序输出。

在递归过程中,我们可以利用字典序的性质,对拓扑排序结果进行调整,从而输出符合字典序要求的结果。

拓扑排序结果的字典序输出是一个比较有意义的问题,在实际工程中也有一定的应用场景。

通过合理的算法设计和编程实现,我们可以方便地得到符合要求的拓扑排序结果,从而更好地解决问题。

希望本文能够帮助读者更好地理解拓扑排序以及相关概念,进一步拓展对图论的认识。

第二篇示例:拓扑排序是一种用于有向图的排序方法,它可以将图中的节点按照一定的顺序排列。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 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 检验图的输出所得结果与预计结果一致。

相关文档
最新文档