拓扑排序课程设计报告[1]
数据结构课程设计
课程设计题目:拓扑排序
姓名:
班级:20091141学号:组长:
指导老师:解德祥
计算机与信息学院
数据结构课程设计
----拓扑排序
一需求分析
1. 问题描述
本次课程设计题目是:用邻接表构造图然后进行拓扑排序,输出拓扑排序序列
拓扑排序的基本思想为:
1). 从有向图中选一个无前驱的顶点输出;
2). 将此顶点和以它为起点的弧删除;
3). 重复1),2) 直到不存在无前驱的顶点;
4). 若此时输出的顶点数小于有向图中的顶点数,则说明有向图中存在回路,否则输出的顶点的顺序即为一个拓扑序列。
2. 基于邻接表的存储结构
入度为零的顶点即为没有前驱的顶点,我们可以附设一个存放各顶点入度的数组indegree[],于是有
(1)找G 中无前驱的顶点——查找indegree[i]为零的顶点vi ;
(2)删除以i 为起点的所有弧——对链在顶点i 后面的所有邻接顶点k ,将对应的indegree[k]减1。
为了避免重复检测入度为零的顶点,可以再设置一个辅助栈,若某一顶点的入度减为0,则将它入栈。每当输出某一入度为0的顶点时,便将它从栈中删除。
3基本要求
(1)输入的形式和输入值的范围;
首先是输入要排序的顶点数和弧数,都为整型,中间用分隔符隔开;再输入各顶点的值,为正型,中间用分隔符隔开;然后输入各条弧的两个顶点值,先输入弧头,再输入弧尾,中间用分隔符隔开,输入的值只能是开始输入的顶点值否则系统会提示输入的值的顶点值不正确,请重新输入,只要继续输入正确的值就行。
(2)输出的形式;
首先输出建立的邻接表,然后是最终各顶点的出度数,再是拓扑排序的序列,并且每输出一个顶点,就会输出一次各顶点的入度数。
(3)程序所能达到的功能;
因为该程序是求拓扑排序,所以算法的功能就是要输出拓扑排序的序列,在一个有向图中,若用顶点表示活动,有向边就表示活动间先后顺序,那么输出的拓扑序列就表示各顶点间的关系为反映出各点的存储结构,以邻接表存储并输出各顶点的入度。
二概要设计
1. 算法中用到的所有各种数据类型的定义
在该程序中用邻接表作为图的存储结构。首先,定义表结点和头结点的结构类型,然后定义图的结构类型。创建图用邻接表存储的函数,其中根据要求输入图的顶点和边数,并根据要求设定每条边的起始位置,构建邻接表依次将顶点插
入到邻接表中。
拓扑排序的函数在该函数中首先要对各顶点求入度,其中要用到求入度的函数,为了避免重复检测入度为零的顶点,设置一个辅助栈,因此要定义顺序栈类型,以及栈的函数:入栈,出栈,判断栈是否为空。
2. 各程序模块之间的层次调用关系
第一部分,void CreatGraph(ALGraph*G)函数构建图,用邻接表存储。这个函数没有调用函数。
第二部分,void TopologicalSort(ALGraph*G)输出拓扑排序函数,这个函数首先调用FindInDegree(G,indegree)对各顶点求入度indegree[0……vernum-1];然后设置了一个辅助栈,调用InitStack(&S)初始化栈,在调用Push(&S,i)入度为0者进栈,while(!StackEmpty(&S))栈不为空时,调用Pop(&sS,&n)输出栈中顶点并将以该顶点为起点的边删除,入度indegree[k]--,当输出某一入度为0的顶点时,便将它从栈中删除。
第三部分,主函数,先后调用void CreatGraph(ALGraph*G)函数构建图、void TopologicalSort(ALGraph*G)函数输出拓扑排序实现整个程序。3. 设计的主程序流程(见附页)
三详细设计
(实现概要设计中定义的所有数据类型, 对每个操作写出伪码算法;对主程序和其他模块也都需要写出伪码算法(伪码算法达到的详细程度建议为;按照伪码算法可以在计算机键盘直接输入高级程序设计语言程序); 写出出函数和过程的调用关系。)
1. 实现概要设计中定义的所有数据类型
#include#include#include#defineMAX_VEXTEX_NUM20//*定义点最大的数值为顶30*//#defineM 20#defineSTACK_INIT_SIZE100//*定义点最大的数值为顶30*//#defineSTACKINCREMENT 10//*定义栈的增量为10*//#defineOK 1#defineERROR 0typedef char ElemType; //*定义栈顶元素类型*//typedef struct ArcNode {intadjvex; //该弧所指向的顶点的位置//struct ArcNode *nextarc;//指向下一条弧的指针//}ArcNode;//*表结点*//typedef struct VNode {chardata; //顶点信息ArcNode *firstarc;//指向第一条依附该顶点的弧的指针//}VNode,AdjList[MAX_VEXTEX_NUM];typedef struct {AdjListvertices;
int vexnum, arcnum; //图的当前顶点数和弧数//}ALGraph;typedef struct //构建栈//{ElemType*base;//*在栈构造之前的指针*//ElemType *top;//*栈顶指针*//int stacksize;//*定义所分配的存储空间*//}SqStack;//*顺序栈*//
2. 算法和各模块的代码
程序中各函数算法思想如下:
2.1void InitStack(SqStack*S)
初始化栈将栈的空间设为STACK-INIT-SIZE 。
2.2int Pop(SqStack*S,ElemType*e)
出栈操作,若站不空,删除S 的栈顶元素,用e 返回其值,并返回OK ;否则返回ERROR 。
2.3void Push(SqStack*S,ElemTypee)
进栈操作,插入元素e 为新的栈顶元素。
2.4int StackEmpty(SqStack*S)
判断栈是否为空, 语句if (S->top=S->base) 判断,若栈不为空,则删除S 的栈顶元素,并返回OK ;否则返回ERROR 。
2.5void CreatGraph (ALGraph*G)
构建图,用邻接表存储,首先定义邻接表指针变量,输入顶点数和弧数,初始化邻接表,将表头向量域置空,输入存在弧的点集合,当输入顶点值超出输入值的范围就会出错,否则依次插入进邻接表,最后输出建立好的邻接表。
2.6void FindInDegree(ALGrapG, int indegreee[])
求入度操作,设一个存放各顶点入度的数组indegreee[],然后
indegreee[i]=0赋初值,for 循环indegreee[]++,存储入度数。2.7void TopologicalISort(ALGraphG)
输出拓扑排序函数。其思路是若G 无回路,则输出G 的顶点的一个拓扑序列并返回OK ,否则返回ERROR 。首先由于邻接表的存储结构入度为零的顶点即为没有前驱的顶点,我们可以附设一个存放个顶点入度的数组,调用FindInDegree(G, indegreee[])对各顶点求入度;为了避免重复检测入度为零0的顶点,设置一个栈,调用InitStack(&S)初始化栈,在调用Push(&S,i)入度为0者进栈,while(!StackEmpty(&S))栈不为空时,调用Pop(&sS,&n)输出栈中顶点并将以该顶点为起点的边删除,入度indegree[k]--,当输出某一入度为0的顶点时,便将它从栈中删除。
3. 算法的时间复杂度和空间复杂度
拓扑排序实际是对邻接表表示的图G 进行遍历的过程,每次访问一个入度为零的顶点,若图G 中没有回路,则需扫描邻接表中的所有边结点,在算法开始时,为建立入度数组D 需访问表头向量中的所有边结点,算法的时间复杂度为O(n+e)。
四测试与分析
对有向无环图【下图】进行拓扑排序。
输入:
结果如下:
五总结
拓扑排序就是对一个有向无环图(DirectedAcyclic Graph 简称DAG)G 进行拓扑排序,是将G 中所有顶点排成一个线性序列,使得图中任意一对顶点u 和v ,若∈E(G),则u 在线性序列中出现在v 之前。
在进行课程设计中,更好的认识了拓扑排序。理清了各个模块之间算法之间的条理。认识了伪代码(Pseudocode )是一种算法描述语言。使用伪代码的目的是为了使被描述的算法可以容易地以任何一种(Pascal ,C ,Java ,etc )实现。因此,伪代码必须结构清晰、代码简单、可读性好,并且类似自然语言。介于自然语言与编程语言之间。它是一种让人便于理解的代码。不依赖于语言的,用来表示程序执行过程,而不一定能编译运行的代码。在讲算法的时候用的很多。
在设计中,我们遇到了程序正确,却对某些无向图无法进行拓扑排序的问题。多次对程序进行修改后,才可以进行拓扑排序。问题出在调用函数的错误理解,模块之间的联系模糊不清。
六附录:
源程序清单
#include#include#include#defineMAX_VEXTEX_NUM20#defineM 20#defineSTACK_INIT_SIZE100#defineSTACKINCREMENT 10//*定义点最大的数值为顶30*////*定义点最大的数值为顶30*////*定义栈的增量为10*//
#defineOK 1#defineERROR 0typedef char ElemType; //*定义栈顶元素类型*//typedef struct ArcNode {int adjvex; //*该弧所指向的顶点的位置*//struct ArcNode *nextarc;//*指向下一条弧的指针*//}ArcNode;//*表结点*//typedef struct VNode {char data; //*顶点信息*//ArcNode *firstarc;//*指向第一条依附该顶点的弧的指针*//}VNode,AdjList[MAX_VEXTEX_NUM];
typedef struct {AdjList vertices; int vexnum, arcnum; //*图的当前顶点数和弧数*//}ALGraph;typedef struct //*构建栈*//{ElemType *base;//*在栈构造之前的指针*//ElemType *top;//*栈顶指针*//int stacksize;//*定义所分配的存储空间*//}SqStack;//*顺序栈*//
void InitStack(SqStack*);//*函数声明*//int Pop(SqStack*,ElemType &);void Push(SqStack*,ElemType); int StackEmpty(SqStack*);void CreatGraph(ALGraph*);void FindInDegree(ALGraph, int *); void TopologicalSort(ALGraph); void InitStack(SqStack*S)//*初始化栈*//{S->base=(ElemType*)malloc(STACK_INIT_SIZE*sizeof(ElemType));//*申请新的结点并由s->base指向*//
if(!S->base)//*存储分配失败*//{printf("memoryallocation failed, goodbye"); exit(1);}S->top=S->base;//*空栈之前的指针赋给头指针*//S->stacksize=STACK_INIT_SIZE;//*栈的空间设为Stack_init_size*//
}int Pop(SqStack*S,ElemType&e)//*出栈操作*//{if(S->top==S->base)//*栈空返回OK*//{return ERROR; //*删除S 的栈顶元素*//}e=*--S->top;return 0; }void Push(SqStack*S,ElemTypee) //*进栈操作,插入元素e 为新的栈顶元素*//{if(S->top-S->base>=S->stacksize){
S->base=(ElemType*)realloc(S->base,(S->stacksize+STACKINCREMENT)*sizeof(ElemType)); if(!S->base)//*存储分配失败*//{printf("memoryallocation failed, goodbye"); exit(1);}S->top=S->base+S->stacksize;S->stacksize+=STACKINCREMENT;}*S->top++=e;}int StackEmpty(SqStack*S)//*判断栈是否为空*//{if(S->top==S->base)//*栈不为空, 则删除S 的栈顶元素, 并返回OK; 否则返回*//return OK; else return ERROR; }int locatevex(ALGraphG,char u) //*若G 图中存在顶点u, 则返回该顶点在图中的位置; 否则返回-2.*//
{int i; for(i=0;i
}void CreatGraph(ALGraph*G)//*构建图建立有向的图的邻接表*//{int i,k; printf("输入结点的个数和弧数(用空格隔开):\n");scanf("%d%d",&G->vexnum,&G->arcnum);printf("输入顶点的值(用空格隔开):\n");for(i=0;ivexnum;i++)//*构造表头向量*//{scanf("%s",&G->vertices[i].data);//*输入顶点的值*//G->vertices[i].firstarc=NULL;//*初始化指针*//}for(k=0;karcnum;k++){char v1,v2; int i,j; loop:printf("输入一条弧的起点和终点(用空格隔开):\n");scanf("%s%s",&v1,&v2);i=locatevex(*G,v1);//*确定v1,v2在G 中的位置*//j=locatevex(*G,v2);if(i==-2||j==-2){printf("输入有误!"); break; goto loop; }ArcNode *p;p =(ArcNode*)malloc(sizeof(ArcNode));if (p==NULL) {printf("memoryallocation failed,goodbey"); exit(1);}p->adjvex=j;//*对弧结点赋值*//p->nextarc=G->vertices[i].firstarc;G->vertices[i].firstarc=p;//*插入到表头向量后面*//}ArcNode *p;//*定义邻接表指针变量*//p =(ArcNode*)malloc(sizeof(ArcNode));if (p==NULL) {}return -2; }exit(OK);
}void FindInDegree(ALGraphG, int indegree[])//*求入度操作*//{int i;
for (i=0; i adjvex]++;G.vertices[i].firstarc=G.vertices[i].firstarc->nextarc;}}}printf("建立的邻接表为:\n");//*输出建立好的邻接表*//for(i=0; i vexnum;i++){printf("%c",G->vertices[i].data);for(p=G->vertices[i].firstarc;p; p =p->nextarc)printf("%3d",p->adjvex);printf("\n");}printf("memoryallocation failed,goodbey"); exit(1);}void TopologicalSort(ALGraphG) //*输出拓扑排序函数。若G 无回路,则输出G 的顶点的一个拓扑序列并返回OK ,否则返回ERROR*//{int indegree[M];int i, k,j; char n; int count =0; ArcNode *p;SqStack S; FindInDegree(G,indegree); //*对各顶点求入度indegree[0..vernum-1]*//InitStack(&S);//*初始化栈*//for (i=0; i
Push(&S,G.vertices[i].data);}printf("进行拓扑排序输出顺序为:");//*输出结果*//while(!StackEmpty(&S)){Pop(&S,n);j=locatevex(G,n);if(j==-2){printf("发生错误, 程序结束."); exit(0);}printf("%4c",G.vertices[j].data);count++;for (p=G.vertices[j].firstarc;p !=NULL; p =p->nextarc){k =p->adjvex;if (!(--indegree[k]))Push(&S,G.vertices[k].data);}}printf("\n");if (count
main()//*主函数*//{printf("********建立有向图的拓扑排序,请按如下要求进行输入*******ALGraph G; //*定义一个图的变量*//CreatGraph(&G);//*调用建图函数*//TopologicalSort(G);//*调用拓扑排序函数*//return 0; }\n");
附流程图:
开始
调用建图函数
输入顶点数、弧数、顶点值输入存在弧的两个顶点建零入度顶点栈S ,入度为0者进栈
!StackEmpty(&S)判断并输出相应信息
结束
Pop(&S,&n)
P=G.vertices[n].firstarc
P!=NULL
K=P->adjves
!(--indegree[k])P=p->nextarcPush(&S,k);